diff --git a/.air.toml b/.air.toml index 87083f809aa..dd0faa01f23 100644 --- a/.air.toml +++ b/.air.toml @@ -7,7 +7,7 @@ exclude_unchanged = true follow_symlink = true include_dir = ["apps", "conf", "devenv/dev-dashboards", "pkg", "public/views"] include_ext = ["go", "ini", "toml", "html", "json"] -stop_on_error = true +stop_on_error = false send_interrupt = true kill_delay = 500 diff --git a/.betterer.results b/.betterer.results index 239af992de5..e6c62adfd30 100644 --- a/.betterer.results +++ b/.betterer.results @@ -901,9 +901,6 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "4"], [0, 0, 0, "Unexpected any. Specify a different type.", "5"] ], - "public/app/core/components/LocalStorageValueProvider/index.tsx:5381": [ - [0, 0, 0, "Do not re-export imported variable (\`./LocalStorageValueProvider\`)", "0"] - ], "public/app/core/components/Login/LoginForm.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"] @@ -978,11 +975,10 @@ exports[`better eslint`] = { [0, 0, 0, "Do not re-export imported variable (\`JsonExplorer\`)", "0"], [0, 0, 0, "Do not re-export imported variable (\`TimeSeries\`)", "1"], [0, 0, 0, "Do not re-export imported variable (\`appEvents\`)", "2"], - [0, 0, 0, "Do not re-export imported variable (\`assignModelProperties\`)", "3"], - [0, 0, 0, "Do not re-export imported variable (\`colors\`)", "4"], - [0, 0, 0, "Do not re-export imported variable (\`contextSrv\`)", "5"], - [0, 0, 0, "Do not re-export imported variable (\`profiler\`)", "6"], - [0, 0, 0, "Do not re-export imported variable (\`updateLegendValues\`)", "7"] + [0, 0, 0, "Do not re-export imported variable (\`colors\`)", "3"], + [0, 0, 0, "Do not re-export imported variable (\`contextSrv\`)", "4"], + [0, 0, 0, "Do not re-export imported variable (\`profiler\`)", "5"], + [0, 0, 0, "Do not re-export imported variable (\`updateLegendValues\`)", "6"] ], "public/app/core/navigation/types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] @@ -3919,9 +3915,6 @@ exports[`better eslint`] = { "public/app/plugins/panel/table/table-new/TableCellOptionEditor.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"] ], - "public/app/plugins/panel/table/table-new/cells/AutoCellOptionsEditor.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"] - ], "public/app/plugins/panel/table/table-new/cells/BarGaugeCellOptionsEditor.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"] @@ -3935,10 +3928,16 @@ exports[`better eslint`] = { [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"] ], + "public/app/plugins/panel/table/table-new/cells/MarkdownCellOptionsEditor.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"] + ], "public/app/plugins/panel/table/table-new/cells/SparklineCellOptionsEditor.tsx:5381": [ [0, 0, 0, "\'VerticalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "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"] ], + "public/app/plugins/panel/table/table-new/cells/TextWrapOptionsEditor.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"] + ], "public/app/plugins/panel/table/table-new/migrations.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], diff --git a/.drone.star b/.drone.star index a430359e27d..a563ee0682a 100644 --- a/.drone.star +++ b/.drone.star @@ -8,7 +8,6 @@ This module returns a Drone configuration including pipelines and secrets. """ load("scripts/drone/events/main.star", "main_pipelines") -load("scripts/drone/events/pr.star", "pr_pipelines") load( "scripts/drone/events/release.star", "publish_artifacts_pipelines", @@ -28,7 +27,6 @@ load("scripts/drone/vault.star", "secrets") def main(_ctx): return ( - pr_pipelines() + main_pipelines() + rrc_patch_pipelines() + publish_image_pipelines_public() + diff --git a/.drone.yml b/.drone.yml index 2ca377f28a9..52bdd9dc125 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,498 +8,6 @@ image_pull_secrets: - gcr - gar kind: pipeline -name: pr-verify-drone -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd - depends_on: [] - environment: - CGO_ENABLED: 0 - image: golang:1.24.5-alpine - name: compile-build-cmd -- commands: - - ./bin/build verify-drone - depends_on: - - compile-build-cmd - image: byrnedo/alpine-curl:0.1.8 - name: lint-drone -trigger: - event: - - pull_request - paths: - exclude: - - docs/** - - '*.md' - include: - - scripts/drone/** - - .drone.yml - - .drone.star -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: pr-verify-starlark -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - go install github.com/bazelbuild/buildtools/buildifier@latest - - buildifier --lint=warn -mode=check -r . - depends_on: [] - image: golang:1.24.5-alpine - name: lint-starlark -trigger: - event: - - pull_request - paths: - exclude: - - docs/** - - '*.md' - include: - - scripts/drone/** - - .drone.star -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: pr-build-e2e -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $(/usr/bin/github-app-external-token) > /github-app/token - environment: - GITHUB_APP_ID: - from_secret: github-app-app-id - GITHUB_APP_INSTALLATION_ID: - from_secret: github-app-installation-id - GITHUB_APP_PRIVATE_KEY: - from_secret: github-app-private-key - failure: ignore - image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59 - name: github-app-generate-token - volumes: - - name: github-app - path: /github-app -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - mkdir -p bin - - curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.1.2/grabpl - - chmod +x bin/grabpl - image: byrnedo/alpine-curl:0.1.8 - name: grabpl -- commands: - - go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd - depends_on: [] - environment: - CGO_ENABLED: 0 - image: golang:1.24.5-alpine - name: compile-build-cmd -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-cue - depends_on: [] - image: golang:1.24.5-alpine - name: verify-gen-cue -- commands: - - '# It is required that generated jsonnet is committed and in sync with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-jsonnet - depends_on: [] - image: golang:1.24.5-alpine - name: verify-gen-jsonnet -- commands: - - yarn install --immutable || yarn install --immutable - depends_on: [] - image: node:22.16.0-alpine - name: yarn-install -- commands: - - apk add --update jq bash - - yarn packages:build - - yarn packages:pack - - ./scripts/validate-npm-packages.sh - depends_on: - - yarn-install - environment: - NODE_OPTIONS: --max_old_space_size=8192 - image: node:22.16.0-alpine - name: build-frontend-packages -- failure: ignore - image: grafana/drone-downstream - name: trigger-enterprise-downstream - settings: - params: - - SOURCE_BUILD_NUMBER=${DRONE_COMMIT} - - SOURCE_COMMIT=${DRONE_COMMIT} - - OSS_PULL_REQUEST=${DRONE_PULL_REQUEST} - repositories: - - grafana/grafana-enterprise@${DRONE_SOURCE_BRANCH} - server: https://drone.grafana.net - token: - from_secret: drone_token -- commands: - - wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz - | tar zx -C /bin - - apk add docker - - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version - - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*' - - docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all - - go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64 - -a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu - -a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7 - -a docker:grafana:linux/arm/v7:ubuntu --yarn-cache=$$YARN_CACHE_FOLDER --build-id=$$DRONE_BUILD_NUMBER - --ubuntu-base=ubuntu-base --alpine-base=alpine-base --tag-format='{{ .version_base - }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{ .version_base }}-{{ .buildID - }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD > packages.txt - - find ./dist -name '*docker*.tar.gz' -type f | xargs -n1 docker load -i - depends_on: - - yarn-install - environment: - _EXPERIMENTAL_DAGGER_CLOUD_TOKEN: - from_secret: dagger_token - image: golang:1.24.5-alpine - name: rgm-package - pull: always - volumes: - - name: docker - path: /var/run/docker.sock -- commands: - - ./bin/grabpl artifacts docker publish --dockerhub-repo grafana/grafana - depends_on: - - rgm-package - environment: - DOCKER_PASSWORD: - from_secret: docker_password - DOCKER_USER: - from_secret: docker_username - GITHUB_APP_ID: "329617" - GITHUB_APP_INSTALLATION_ID: "37346161" - GITHUB_APP_PRIVATE_KEY: - from_secret: delivery-bot-app-private-key - failure: ignore - image: google/cloud-sdk:431.0.0 - name: publish-images-grafana - volumes: - - name: docker - path: /var/run/docker.sock -- commands: - - yarn e2e:plugin:build - depends_on: - - yarn-install - environment: - NODE_OPTIONS: --max_old_space_size=8192 - image: node:22.16.0-alpine - name: build-test-plugins -- commands: - - apk add --update tar bash - - mkdir grafana - - tar --strip-components=1 -xvf ./dist/*amd64.tar.gz -C grafana - - cp -r devenv scripts tools grafana && cd grafana && ./scripts/grafana-server/start-server - depends_on: - - rgm-package - detach: true - environment: - GF_APP_MODE: development - GF_SERVER_HTTP_PORT: "3001" - GF_SERVER_ROUTER_LOGGING: "1" - image: alpine:3.21.3 - name: grafana-server -- commands: - - GITHUB_TOKEN=$(cat /github-app/token) - - cd / - - ./cpp-e2e/scripts/ci-run.sh azure ${DRONE_SOURCE_BRANCH} - depends_on: - - grafana-server - - github-app-generate-token - environment: - AZURE_SP_APP_ID: - from_secret: azure_sp_app_id - AZURE_SP_PASSWORD: - from_secret: azure_sp_app_pw - AZURE_TENANT: - from_secret: azure_tenant - CYPRESS_CI: "true" - HOST: grafana-server - image: us-docker.pkg.dev/grafanalabs-dev/docker-oss-plugin-partnerships-dev/e2e-14.3.2:1.0.0 - name: end-to-end-tests-cloud-plugins-suite-azure - volumes: - - name: github-app - path: /github-app - when: - paths: - include: - - pkg/tsdb/azuremonitor/** - - public/app/plugins/datasource/azuremonitor/** - - e2e/cloud-plugins-suite/azure-monitor.spec.ts - repo: - - grafana/grafana -- commands: - - npx wait-on@7.0.1 http://$HOST:$PORT - - yarn playwright install --with-deps chromium - - GRAFANA_URL=http://$HOST:$PORT yarn e2e:playwright --grep @plugins - depends_on: - - grafana-server - - build-test-plugins - environment: - HOST: grafana-server - PORT: "3001" - PROV_DIR: /grafana/scripts/grafana-server/tmp/conf/provisioning - image: node:22-bookworm - name: playwright-plugin-e2e -- commands: - - apt-get update - - apt-get install -yq zip - - printenv GCP_GRAFANA_UPLOAD_ARTIFACTS_KEY > /tmp/gcpkey_upload_artifacts.json - - gcloud auth activate-service-account --key-file=/tmp/gcpkey_upload_artifacts.json - - gsutil cp -r ./playwright-report/. gs://releng-pipeline-artifacts-dev/${DRONE_BUILD_NUMBER}/playwright-report - - export E2E_PLAYWRIGHT_REPORT_URL=https://storage.googleapis.com/releng-pipeline-artifacts-dev/${DRONE_BUILD_NUMBER}/playwright-report/index.html - - "echo \"E2E Playwright report uploaded to: \n $${E2E_PLAYWRIGHT_REPORT_URL}\"" - depends_on: - - playwright-plugin-e2e - environment: - GCP_GRAFANA_UPLOAD_ARTIFACTS_KEY: - from_secret: gcp_upload_artifacts_key - failure: ignore - image: google/cloud-sdk:431.0.0 - name: playwright-e2e-report-upload - when: - status: - - success - - failure -- commands: - - GITHUB_TOKEN=$(cat /github-app/token) - - if [ ! -d ./playwright-report/trace ]; then echo 'all tests passed'; exit 0; fi - - export E2E_PLAYWRIGHT_REPORT_URL=https://storage.googleapis.com/releng-pipeline-artifacts-dev/${DRONE_BUILD_NUMBER}/playwright-report/index.html - - 'curl -L -X POST https://api.github.com/repos/grafana/grafana/issues/${DRONE_PULL_REQUEST}/comments - -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $${GITHUB_TOKEN}" - -H "X-GitHub-Api-Version: 2022-11-28" -d "{\"body\":\"❌ Failed to run Playwright - plugin e2e tests.

Click [here]($${E2E_PLAYWRIGHT_REPORT_URL}) to - browse the Playwright report and trace viewer.
For information on how to - run Playwright tests locally, refer to the [Developer guide](https://github.com/grafana/grafana/blob/main/contribute/developer-guide.md#to-run-the-playwright-tests). - \"}"' - depends_on: - - playwright-e2e-report-upload - - github-app-generate-token - failure: ignore - image: byrnedo/alpine-curl:0.1.8 - name: playwright-e2e-report-post-link - volumes: - - name: github-app - path: /github-app - when: - status: - - success - - failure -- commands: - - export GITHUB_TOKEN=$(cat /github-app/token) - - if [ -z `find ./e2e -type f -name *spec.ts.mp4` ]; then echo 'no e2e videos found - from remaining tests'; exit 0; fi - - apt-get update - - apt-get install -yq zip - - printenv GCP_GRAFANA_UPLOAD_ARTIFACTS_KEY > /tmp/gcpkey_upload_artifacts.json - - gcloud auth activate-service-account --key-file=/tmp/gcpkey_upload_artifacts.json - - find ./e2e -type f -name "*spec.ts.mp4" | zip e2e/videos.zip -@ - - gsutil cp e2e/videos.zip gs://$${E2E_TEST_ARTIFACTS_BUCKET}/${DRONE_BUILD_NUMBER}/artifacts/videos/videos.zip - - export E2E_ARTIFACTS_VIDEO_ZIP=https://storage.googleapis.com/$${E2E_TEST_ARTIFACTS_BUCKET}/${DRONE_BUILD_NUMBER}/artifacts/videos/videos.zip - - 'echo "E2E Test artifacts uploaded to: $${E2E_ARTIFACTS_VIDEO_ZIP}"' - - 'curl -X POST https://api.github.com/repos/${DRONE_REPO}/statuses/${DRONE_COMMIT_SHA} - -H "Authorization: token $${GITHUB_TOKEN}" -d "{\"state\":\"success\",\"target_url\":\"$${E2E_ARTIFACTS_VIDEO_ZIP}\", - \"description\": \"Click on the details to download e2e recording videos\", \"context\": - \"e2e_artifacts\"}"' - depends_on: - - end-to-end-tests-cloud-plugins-suite-azure - - playwright-plugin-e2e - - github-app-generate-token - environment: - E2E_TEST_ARTIFACTS_BUCKET: releng-pipeline-artifacts-dev - GCP_GRAFANA_UPLOAD_ARTIFACTS_KEY: - from_secret: gcp_upload_artifacts_key - failure: ignore - image: google/cloud-sdk:431.0.0 - name: e2e-tests-artifacts-upload - volumes: - - name: github-app - path: /github-app - when: - status: - - success - - failure -- commands: - - yarn storybook:build - - ./bin/build verify-storybook - depends_on: - - rgm-package - - build-frontend-packages - environment: - NODE_OPTIONS: --max_old_space_size=4096 - image: node:22.16.0-alpine - name: build-storybook - when: - paths: - include: - - packages/grafana-ui/** -- commands: - - npx wait-on@7.0.1 http://$HOST:$PORT - - pa11y-ci --config e2e/pa11yci.conf.js - depends_on: - - grafana-server - environment: - GRAFANA_MISC_STATS_API_KEY: - from_secret: grafana_misc_stats_api_key - HOST: grafana-server - NO_THRESHOLDS: "false" - PORT: 3001 - failure: always - image: grafana/docker-puppeteer:1.1.0 - name: test-a11y-frontend -trigger: - event: - - pull_request - paths: - exclude: - - '*.md' - - docs/** - - latest.json -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker -- name: github-app - temp: {} ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline -name: pr-docs -node: - type: no-parallel -platform: - arch: amd64 - os: linux -services: [] -steps: -- commands: - - echo $DRONE_RUNNER_NAME - image: alpine:3.21.3 - name: identify-runner -- commands: - - yarn install --immutable || yarn install --immutable - depends_on: [] - image: node:22.16.0-alpine - name: yarn-install -- commands: - - yarn run prettier:checkDocs - depends_on: - - yarn-install - environment: - NODE_OPTIONS: --max_old_space_size=8192 - image: node:22.16.0-alpine - name: lint-docs -- commands: - - mkdir -p /hugo/content/docs/grafana/latest - - 'echo -e ''---\nredirectURL: /docs/grafana/latest/\ntype: redirect\nversioned: - true\n---\n'' > /hugo/content/docs/grafana/_index.md' - - cp -r docs/sources/* /hugo/content/docs/grafana/latest/ - - cd /hugo && make prod - image: grafana/docs-base:latest - name: build-docs-website - pull: always -- commands: - - '# It is required that code generated from Thema/CUE be committed and in sync - with its inputs.' - - '# The following command will fail if running code generators produces any diff - in output.' - - apk add --update make - - CODEGEN_VERIFY=1 make gen-cue - depends_on: [] - image: golang:1.24.5-alpine - name: verify-gen-cue -trigger: - event: - - pull_request - paths: - include: - - '*.md' - - docs/** - - packages/**/*.md - - latest.json - repo: - - grafana/grafana -type: docker -volumes: -- host: - path: /var/run/docker.sock - name: docker ---- -clone: - retries: 3 -depends_on: [] -environment: - EDITION: oss -image_pull_secrets: -- gcr -- gar -kind: pipeline name: main-docs node: type: no-parallel @@ -2365,8 +1873,3 @@ get: path: secret/data/common/gcr kind: secret name: gcr_credentials ---- -kind: signature -hmac: bfbdbe4aeedbcdc8778dd67abdfe94a75a20bdb314d1c53418cf2c2ea7a9f0c9 - -... diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f26cfd876aa..aec3137d4b1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -73,7 +73,7 @@ /.air.toml @macabu # Git Sync / App Platform Provisioning -/pkg/apis/provisioning @grafana/grafana-git-ui-sync-team +/apps/provisioning/ @grafana/grafana-git-ui-sync-team /public/app/features/provisioning @grafana/grafana-git-ui-sync-team /pkg/registry/apis/provisioning @grafana/grafana-git-ui-sync-team /pkg/tests/apis/provisioning @grafana/grafana-git-ui-sync-team @@ -83,6 +83,7 @@ /apps/dashboard/ @grafana/grafana-app-platform-squad @grafana/dashboards-squad /apps/folder/ @grafana/grafana-app-platform-squad /apps/playlist/ @grafana/grafana-app-platform-squad +/apps/shorturl/ @grafana/sharing-squad /apps/secret/ @grafana/grafana-operator-experience-squad /apps/investigations/ @fcjack @matryer @svennergr /apps/advisor/ @grafana/plugins-platform-backend @@ -411,6 +412,39 @@ /e2e/cloud-plugins-suite/ @grafana/partner-datasources /e2e-playwright/ @grafana/grafana-frontend-platform /e2e-playwright/dashboard-new-layouts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/dashboard-browse-nested.spec.ts @grafana/grafana-search-navigate-organise +/e2e-playwright/dashboards-suite/dashboard-browse.spec.ts @grafana/grafana-search-navigate-organise +/e2e-playwright/dashboards-suite/dashboard-export-image.spec.ts @grafana/sharing-squad +/e2e-playwright/dashboards-suite/dashboard-export-json.spec.ts @grafana/sharing-squad +/e2e-playwright/dashboards-suite/dashboard-keybindings.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/dashboard-links-without-slug.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/dashboard-live-streaming.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/dashboard-public-create.spec.ts @grafana/grafana-operator-experience-squad +/e2e-playwright/dashboards-suite/dashboard-public-templating.spec.ts @grafana/grafana-operator-experience-squad +/e2e-playwright/dashboards-suite/dashboard-share-externally-create.spec.ts @grafana/sharing-squad +/e2e-playwright/dashboards-suite/dashboard-share-internally.spec.ts @grafana/sharing-squad +/e2e-playwright/dashboards-suite/dashboard-share-snapshot-create.spec.ts @grafana/sharing-squad +/e2e-playwright/dashboards-suite/dashboard-templating.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/dashboard-time-zone.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/dashboard-timepicker.spec.ts @grafana/grafana-frontend-platform +/e2e-playwright/dashboards-suite/embedded-dashboard.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/general-dashboards.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/import-dashboard.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/load-options-from-url.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/new-constant-variable.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/new-custom-variable.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/new-datasource-variable.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/new-interval-variable.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/new-query-variable.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/new-text-box-variable.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/repeating-a-panel-horizontally.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/repeating-a-panel-vertically.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/repeating-an-empty-row.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/set-options-from-ui.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/snapshot-create.spec.ts @grafana/sharing-squad +/e2e-playwright/dashboards-suite/templating-dashboard-links-and-variables.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/textbox-variables.spec.ts @grafana/dashboards-squad +/e2e-playwright/dashboards-suite/utils/makeDashboard.ts @grafana/grafana-search-navigate-organise /e2e-playwright/panels-suite/dashlist.spec.ts @grafana/grafana-search-navigate-organise /e2e-playwright/panels-suite/datagrid-data-change.spec.ts @grafana/dataviz-squad /e2e-playwright/panels-suite/datagrid-editing-features.spec.ts @grafana/dataviz-squad @@ -471,7 +505,7 @@ /packages/grafana-data/src/utils/binaryOperators.ts @grafana/datapro /packages/grafana-data/src/utils/csv* @grafana/dataviz-squad /packages/grafana-data/src/utils/dataLinks* @grafana/dashboards-squad -/packages/grafana-data/src/utils/datasource.ts @grafana/grafana-datasources-core-services +/packages/grafana-data/src/utils/datasource* @grafana/grafana-datasources-core-services /packages/grafana-data/src/utils/docs.ts @grafana/grafana-frontend-platform /packages/grafana-data/src/utils/deprecationWarning* @grafana/grafana-frontend-platform /packages/grafana-data/src/utils/featureToggles.ts @grafana/grafana-frontend-platform @@ -678,23 +712,143 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform # public folder /public/app/api/ @grafana/grafana-frontend-platform -/public/app/core/ @grafana/grafana-frontend-platform -/public/app/core/components/TimePicker/ @grafana/grafana-frontend-platform -/public/app/core/components/Layers/ @grafana/dataviz-squad -/public/app/core/components/GraphNG/ @grafana/dataviz-squad -/public/app/core/components/TimeSeries/ @grafana/dataviz-squad -/public/app/core/components/TimelineChart/ @grafana/dataviz-squad +/public/app/core/actions/ @grafana/grafana-frontend-platform +/public/app/core/app_events.ts @grafana/grafana-frontend-platform +/public/app/core/components/AccessControl/ @grafana/identity-access-team +/public/app/core/components/Animations/ @grafana/grafana-frontend-platform +/public/app/core/components/AppNotifications/ @grafana/grafana-search-navigate-organise +/public/app/core/components/AppChrome/ @grafana/grafana-search-navigate-organise +/public/app/core/components/Branding/ @grafana/grafana-frontend-platform +/public/app/core/components/Breadcrumbs/ @grafana/grafana-search-navigate-organise +/public/app/core/components/BouncingLoader/ @grafana/grafana-frontend-platform +/public/app/core/components/CardButton.tsx @grafana/dashboards-squad +/public/app/core/components/CloseButton/ @grafana/grafana-frontend-platform +/public/app/core/components/ColorScale/ @grafana/dataviz-squad +/public/app/core/components/DynamicImports/ @grafana/grafana-search-navigate-organise +/public/app/core/components/EmptyListCTA/ @grafana/grafana-frontend-platform +/public/app/core/components/FolderFilter/ @grafana/sharing-squad +/public/app/core/components/Footer/ @grafana/grafana-search-navigate-organise +/public/app/core/components/ForgottenPassword/ @grafana/grafana-search-navigate-organise /public/app/core/components/Form/ @grafana/grafana-frontend-platform +/public/app/core/components/FormPrompt/ @grafana/grafana-frontend-platform +/public/app/core/components/GraphNG/ @grafana/dataviz-squad +/public/app/core/components/help/ @grafana/grafana-search-navigate-organise +/public/app/core/components/Indent/ @grafana/grafana-search-navigate-organise +/public/app/core/components/InviteUserButton/ @grafana/sharing-squad +/public/app/core/components/Layers/ @grafana/dataviz-squad +/public/app/core/components/Login/ @grafana/grafana-search-navigate-organise +/public/app/core/components/NativeScrollbar.tsx @grafana/grafana-search-navigate-organise +/public/app/core/components/NavLandingPage/ @grafana/grafana-search-navigate-organise +/public/app/core/components/NestedFolderPicker/ @grafana/grafana-search-navigate-organise /public/app/core/components/OptionsUI/ @grafana/dashboards-squad @grafana/dataviz-squad +/public/app/core/components/Page/ @grafana/grafana-search-navigate-organise +/public/app/core/components/PageActionBar/ @grafana/grafana-search-navigate-organise +/public/app/core/components/PageInfo/ @grafana/grafana-search-navigate-organise +/public/app/core/components/PageLoader/ @grafana/grafana-search-navigate-organise +/public/app/core/components/PageNotFound/ @grafana/grafana-search-navigate-organise +/public/app/core/components/PanelTypeFilter/ @grafana/sharing-squad +/public/app/core/components/PasswordField/ @grafana/grafana-frontend-platform +/public/app/core/components/PluginHelp/ @grafana/plugins-platform-frontend +/public/app/core/components/QueryOperationRow/ @grafana/dashboards-squad +/public/app/core/components/RolePickerDrawer/ @grafana/identity-access-team +/public/app/core/components/Select/ @grafana/grafana-frontend-platform +/public/app/core/components/SharedPreferences/ @grafana/grafana-frontend-platform +/public/app/core/components/Signup/ @grafana/grafana-search-navigate-organise +/public/app/core/components/SplitPaneWrapper/ @grafana/observability-traces-and-profiling +/public/app/core/components/SVG/ @grafana/dataviz-squad +/public/app/core/components/TagFilter/ @grafana/grafana-frontend-platform +/public/app/core/components/Theme/ @grafana/grafana-frontend-platform +/public/app/core/components/ThemeSelector/ @grafana/grafana-frontend-platform +/public/app/core/components/TimelineChart/ @grafana/dataviz-squad +/public/app/core/components/TimePicker/ @grafana/grafana-frontend-platform +/public/app/core/components/TimeSeries/ @grafana/dataviz-squad +/public/app/core/components/Upgrade/ @grafana/grafana-operator-experience-squad +/public/app/core/components/ValidationLabels/ @grafana/grafana-search-navigate-organise +/public/app/core/components/VersionHistory/ @grafana/dashboards-squad +/public/app/core/config.ts @grafana/grafana-frontend-platform +/public/app/core/constants.ts @grafana/grafana-frontend-platform +/public/app/core/context/ @grafana/grafana-frontend-platform +/public/app/core/copy/appNotification.ts @grafana/grafana-search-navigate-organise +/public/app/core/core.ts @grafana/grafana-frontend-platform +/public/app/core/crash/ @grafana/observability-traces-and-profiling +/public/app/core/history/ @grafana/observability-traces-and-profiling +/public/app/core/hooks/useBusEvent.ts @grafana/grafana-frontend-platform +/public/app/core/hooks/useCleanup.ts @grafana/grafana-frontend-platform +/public/app/core/hooks/useMediaQueryMinWidth.ts @grafana/grafana-frontend-platform +/public/app/core/hooks/useNavModel.ts @grafana/grafana-search-navigate-organise +/public/app/core/hooks/useQueryParams.ts @grafana/grafana-frontend-platform +/public/app/core/icons/ @grafana/grafana-frontend-platform +/public/app/core/lifecycle-hooks.ts @grafana/grafana-frontend-platform +/public/app/core/log_events.ts @grafana/dashboards-squad +/public/app/core/monacoEnv.ts @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend +/public/app/core/navigation/ @grafana/grafana-search-navigate-organise +/public/app/core/profiler.ts @grafana/dashboards-squad +/public/app/core/reducers/ @grafana/grafana-frontend-platform +/public/app/core/selectors/ @grafana/grafana-frontend-platform +/public/app/core/services/__mocks__/backend_srv.ts @grafana/grafana-frontend-platform +/public/app/core/services/backend_srv.ts @grafana/grafana-frontend-platform +/public/app/core/services/context_srv.ts @grafana/grafana-frontend-platform +/public/app/core/services/CorrelationsService.ts @grafana/datapro +/public/app/core/services/echo/ @grafana/grafana-frontend-platform +/public/app/core/services/FetchQueue* @grafana/grafana-frontend-platform +/public/app/core/services/impression_srv.ts @grafana/grafana-frontend-platform +/public/app/core/services/KeybindingSet* @grafana/grafana-frontend-platform +/public/app/core/services/keybindingSrv.ts @grafana/grafana-frontend-platform +/public/app/core/services/mocks/subscribeTester.ts @grafana/grafana-frontend-platform +/public/app/core/services/mousetrap/ @grafana/grafana-frontend-platform +/public/app/core/services/NewFrontendAssetsChecker* @grafana/grafana-frontend-platform +/public/app/core/services/PreferencesService.ts @grafana/grafana-frontend-platform +/public/app/core/services/ResponseQueue* @grafana/grafana-frontend-platform +/public/app/core/services/StateManagerBase.ts @grafana/grafana-frontend-platform +/public/app/core/services/theme.ts @grafana/grafana-frontend-platform +/public/app/core/specs/backend_srv.test.ts @grafana/grafana-frontend-platform +/public/app/core/specs/factors.test.ts @grafana/dashboards-squad +/public/app/core/specs/flatten.test.ts @grafana/grafana-frontend-platform +/public/app/core/specs/impression_srv.test.ts @grafana/grafana-frontend-platform +/public/app/core/specs/rangeutil.test.ts @grafana/grafana-frontend-platform +/public/app/core/specs/store.test.ts @grafana/grafana-frontend-platform +/public/app/core/specs/TableModel.test.ts @grafana/partner-datasources +/public/app/core/specs/ticks.test.ts @grafana/plugins-platform-frontend +/public/app/core/specs/time_series.test.ts @grafana/dataviz-squad +/public/app/core/store.ts @grafana/grafana-frontend-platform +/public/app/core/TableModel.ts @grafana/partner-datasources +/public/app/core/time_series2.ts @grafana/dataviz-squad +/public/app/core/trustedTypePolicies.ts @grafana/grafana-frontend-platform +/public/app/core/utils/accessControl.ts @grafana/identity-access-team +/public/app/core/utils/applyStateChanges.ts @grafana/dashboards-squad +/public/app/core/utils/arrayMove.ts @grafana/grafana-frontend-platform +/public/app/core/utils/auth.ts @grafana/identity-access-team +/public/app/core/utils/browser* @grafana/grafana-frontend-platform +/public/app/core/utils/colors.ts @grafana/grafana-frontend-platform +/public/app/core/utils/ConfigProvider.tsx @grafana/grafana-frontend-platform +/public/app/core/utils/CorsSharedWorker.ts @grafana/grafana-frontend-platform +/public/app/core/utils/CorsWorker.ts @grafana/grafana-frontend-platform +/public/app/core/utils/connectWithReduxStore.tsx @grafana/grafana-frontend-platform +/public/app/core/utils/dag* @grafana/grafana-frontend-platform +/public/app/core/utils/deferred.ts @grafana/grafana-frontend-platform +/public/app/core/utils/docsLinks.ts @grafana/grafana-frontend-platform +/public/app/core/utils/errors* @grafana/grafana-frontend-platform +/public/app/core/utils/explore* @grafana/observability-traces-and-profiling +/public/app/core/utils/factors.ts @grafana/dashboards-squad +/public/app/core/utils/fetch* @grafana/grafana-frontend-platform +/public/app/core/utils/flatten.ts @grafana/grafana-frontend-platform +/public/app/core/utils/isShallowEqual.ts @grafana/grafana-frontend-platform +/public/app/core/utils/kbn* @grafana/grafana-frontend-platform +/public/app/core/utils/navBarItem-translations.ts @grafana/grafana-search-navigate-organise +/public/app/core/utils/object* @grafana/grafana-frontend-platform +/public/app/core/utils/query* @grafana/grafana-datasources-core-services +/public/app/core/utils/richHistory* @grafana/observability-traces-and-profiling +/public/app/core/utils/set.ts @grafana/grafana-frontend-platform +/public/app/core/utils/shortLinks* @grafana/sharing-squad +/public/app/core/utils/ticks.ts @grafana/plugins-platform-frontend +/public/app/core/utils/timePicker* @grafana/grafana-frontend-platform +/public/app/core/utils/timeRegions* @grafana/dataviz-squad +/public/app/core/utils/urlToken.ts @grafana/identity-access-team +/public/app/core/utils/version.ts @grafana/partner-datasources /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 - -# Temp owners until Enterprise team takes over -/public/app/features/migrate-to-cloud @grafana/grafana-frontend-platform - /public/app/features/actions/ @grafana/dataviz-squad +/public/app/features/admin/ @grafana/identity-access-team /public/app/features/auth-config/ @grafana/identity-squad /public/app/features/annotations/ @grafana/dashboards-squad /public/app/features/canvas/ @grafana/dataviz-squad @@ -718,6 +872,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform /public/app/features/live/ @grafana/dashboards-squad /public/app/features/apiserver/ @grafana/grafana-app-platform-squad /public/app/features/manage-dashboards/ @grafana/dashboards-squad +/public/app/features/migrate-to-cloud @grafana/grafana-operator-experience-squad /public/app/features/notifications/ @grafana/grafana-search-navigate-organise /public/app/features/org/ @grafana/grafana-search-navigate-organise /public/app/features/panel/ @grafana/dashboards-squad diff --git a/.github/workflows/issue-opened.yml b/.github/workflows/issue-opened.yml index b54bb968b7f..2ca46dd1db2 100644 --- a/.github/workflows/issue-opened.yml +++ b/.github/workflows/issue-opened.yml @@ -88,7 +88,6 @@ jobs: private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} permission-members: read permission-issues: write - - name: Check if member of grafana org id: check-if-grafana-org-member continue-on-error: true @@ -96,6 +95,13 @@ jobs: env: GH_TOKEN: ${{ steps.generate_token.outputs.token }} ACTOR: ${{ github.actor }} + - name: Checkout + if: steps.check-if-grafana-org-member.outputs.is_grafana_org_member != 'true' && github.event.issue.author_association != 'MEMBER' && github.event.issue.author_association != 'OWNER' + uses: actions/checkout@v4 # v4.2.2 + with: + persist-credentials: false + sparse-checkout: | + .github/workflows/auto-triager - name: Send issue to the auto triager action id: auto_triage if: steps.check-if-grafana-org-member.outputs.is_grafana_org_member != 'true' && github.event.issue.author_association != 'MEMBER' && github.event.issue.author_association != 'OWNER' diff --git a/.github/workflows/pr-e2e-tests.yml b/.github/workflows/pr-e2e-tests.yml index 6ceff2b9d45..824b621d198 100644 --- a/.github/workflows/pr-e2e-tests.yml +++ b/.github/workflows/pr-e2e-tests.yml @@ -44,8 +44,6 @@ jobs: runs-on: ubuntu-latest-16-cores permissions: contents: read - outputs: - artifact: ${{ steps.artifact.outputs.artifact }} steps: - uses: actions/checkout@v4 with: @@ -57,7 +55,7 @@ jobs: - uses: actions/cache@v4 id: cache with: - key: "build-grafana-${{ runner.os }}-${{ hashFiles('yarn.lock', 'public/*', 'packages/*', 'pkg/**/*.go', '**/go.mod', '**/go.sum', '!**_test.go', '!**.test.ts', '!**.test.tsx') }}" + key: "build-grafana-${{ runner.os }}-${{ hashFiles('yarn.lock', 'public/*', 'packages/*', 'pkg/**/*.go', '**/go.mod', '**/go.sum', '!**_test.go', '!**.test.ts', '!**.test.tsx', 'Dockerfile') }}" path: | build-dir @@ -67,15 +65,16 @@ jobs: uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e with: verb: run - args: go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir="${PWD}" > out.txt + args: go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 -a docker:grafana:linux/amd64 --grafana-dir="${PWD}" > out.txt - name: Cat built artifact if: steps.cache.outputs.cache-hit != 'true' run: cat out.txt - - name: Move built artifact + - name: Move built artifacts if: steps.cache.outputs.cache-hit != 'true' run: | mkdir -p build-dir - mv "$(cat out.txt)" build-dir/grafana.tar.gz + mv "$(grep 'grafana_.*tar.gz$' out.txt | grep -Fv -m1 'docker')" build-dir/grafana.tar.gz + mv "$(grep 'grafana_.*docker.tar.gz$' out.txt)" build-dir/grafana.docker.tar.gz # If cache hit, validate the artifact is present - name: Validate artifact @@ -90,14 +89,20 @@ jobs: run: echo "artifact=grafana-server-${{github.run_number}}" >> "$GITHUB_OUTPUT" id: artifact - - name: Upload artifact + - name: Upload grafana.tar.gz uses: actions/upload-artifact@v4 - id: upload with: retention-days: 1 - name: ${{ steps.artifact.outputs.artifact }} + name: grafana-tar-gz path: build-dir/grafana.tar.gz + - name: Upload grafana docker tarball + uses: actions/upload-artifact@v4 + with: + retention-days: 1 + name: grafana-docker-tar-gz + path: build-dir/grafana.docker.tar.gz + # TODO: we won't need this when we only have playwright build-e2e-runner: needs: detect-changes @@ -131,6 +136,66 @@ jobs: name: ${{ steps.artifact.outputs.artifact }} path: e2e-runner + push-docker-image: + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false + permissions: + contents: read + id-token: write + runs-on: ubuntu-latest + needs: + - build-grafana + steps: + - id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main + with: + repo_secrets: | + GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a + with: + app_id: ${{ vars.DELIVERY_BOT_APP_ID }} + private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }} + repositories: '["grafana"]' + permissions: '{"checks": "write"}' + - uses: grafana/shared-workflows/actions/login-to-gar@main + id: login-to-gar + with: + registry: 'us-docker.pkg.dev' + environment: 'dev' + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 + with: + name: grafana-docker-tar-gz + path: . + - name: Load & Push Docker image + env: + BUILD_ID: ${{ github.run_id }} + run: | + set -euo pipefail + LOADED_IMAGE_NAME=$(docker load -i grafana.docker.tar.gz | sed 's/Loaded image: //g') + VERSION=$(echo "${LOADED_IMAGE_NAME}" | cut -d ':' -f 2 | cut -d '-' -f 1) + DOCKER_IMAGE="us-docker.pkg.dev/grafanalabs-dev/docker-grafana-dev/grafana:${VERSION}-${BUILD_ID}" + docker tag "${LOADED_IMAGE_NAME}" "${DOCKER_IMAGE}" + docker push "${DOCKER_IMAGE}" + echo "IMAGE=${DOCKER_IMAGE}" >> "$GITHUB_ENV" + - name: Add PR status check + env: + GH_TOKEN: ${{ steps.generate_token.outputs.token }} + SHA: ${{ github.event.pull_request.head.sha }} + run: | + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/grafana/grafana/check-runs \ + -f "name=${IMAGE}" \ + -f "head_sha=${SHA}" \ + -f 'status=completed' \ + -f 'conclusion=neutral' \ + -f 'output[title]=Docker image' \ + -f "output[summary]=${IMAGE}" \ + -f "output[text]=${IMAGE}" + run-e2e-tests: needs: - build-grafana @@ -141,8 +206,6 @@ jobs: include: - suite: various-suite path: e2e/various-suite - - suite: dashboards-suite - path: e2e/dashboards-suite - suite: various-suite (old arch) path: e2e/old-arch/various-suite flags: --flags="--env dashboardScene=false" @@ -166,7 +229,7 @@ jobs: persist-credentials: false - uses: actions/download-artifact@v4 with: - name: ${{ needs.build-grafana.outputs.artifact }} + name: grafana-tar-gz - uses: actions/download-artifact@v4 with: name: ${{ needs.build-e2e-runner.outputs.artifact }} @@ -242,7 +305,7 @@ jobs: persist-credentials: false - uses: actions/download-artifact@v4 with: - name: ${{ needs.build-grafana.outputs.artifact }} + name: grafana-tar-gz - name: Run E2E tests uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e with: @@ -354,7 +417,7 @@ jobs: persist-credentials: false - uses: actions/download-artifact@v4 with: - name: ${{ needs.build-grafana.outputs.artifact }} + name: grafana-tar-gz - name: Run PR a11y test if: github.event_name == 'pull_request' uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 231bb0395c2..33288a4f971 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -1,6 +1,12 @@ name: Build Release Packages on: workflow_dispatch: + inputs: + source-event: + description: If this workflow was triggered by another workflow, this value should be set to the GITHUB_EVENT_NAME of that source workflow. + type: string + required: false + default: workflow_dispatch schedule: # Every weeknight at midnight # "Scheduled workflows will only run on the default branch." (docs.github.com) diff --git a/.github/workflows/run-schema-v2-e2e.yml b/.github/workflows/run-schema-v2-e2e.yml index 2430da2f905..30b4e47efc7 100644 --- a/.github/workflows/run-schema-v2-e2e.yml +++ b/.github/workflows/run-schema-v2-e2e.yml @@ -13,7 +13,7 @@ env: jobs: dashboard-schema-v2-e2e: - runs-on: ubuntu-latest + runs-on: ubuntu-latest-8-cores continue-on-error: true if: github.event.pull_request.draft == false steps: @@ -32,12 +32,10 @@ jobs: cache: 'yarn' - name: Install dependencies run: yarn install --immutable + - name: Install Playwright browsers + run: yarn playwright install --with-deps chromium - name: Build grafana run: make build - - name: Install Cypress dependencies - uses: cypress-io/github-action@b8ba51a856ba5f4c15cf39007636d4ab04f23e3c - with: - runTests: false - name: Run dashboard scenes e2e run: yarn e2e:schema-v2 || echo "Test failed but marking as success since schema V2 is behind a feature flag and should not block PRs" diff --git a/.prettierignore b/.prettierignore index 8fd0b837b78..e5b7bd9ca75 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,7 +5,6 @@ build compiled data deployment_tools_config.json -devenv dist e2e/tmp node_modules @@ -40,3 +39,7 @@ public/mockServiceWorker.js /.nx/cache /.nx/workspace-data + +# Playwright results +test-results +playwright-report \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index ddc832d65c9..e7c0ec0fadb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -83,6 +83,8 @@ COPY pkg/storage/unified/apistore pkg/storage/unified/apistore COPY pkg/semconv pkg/semconv COPY pkg/aggregator pkg/aggregator COPY apps/playlist apps/playlist +COPY apps/shorturl apps/shorturl +COPY apps/provisioning apps/provisioning COPY apps/secret apps/secret COPY apps/investigations apps/investigations COPY apps/advisor apps/advisor diff --git a/apps/advisor/go.mod b/apps/advisor/go.mod index 2a232b42873..b6f7f8b800c 100644 --- a/apps/advisor/go.mod +++ b/apps/advisor/go.mod @@ -10,7 +10,7 @@ require ( github.com/grafana/grafana-app-sdk v0.40.2 github.com/grafana/grafana-app-sdk/logging v0.40.1 github.com/grafana/grafana-plugin-sdk-go v0.278.0 - github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250725144121-b1592b5e36d2 + github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 github.com/stretchr/testify v1.10.0 k8s.io/apimachinery v0.33.3 k8s.io/apiserver v0.33.3 @@ -106,7 +106,7 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/validate v0.24.0 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect - github.com/go-sql-driver/mysql v1.9.2 // indirect + github.com/go-sql-driver/mysql v1.9.3 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.5 // indirect @@ -130,12 +130,13 @@ require ( github.com/grafana/authlib v0.0.0-20250710201142-9542f2f28d43 // indirect github.com/grafana/dataplane/sdata v0.0.9 // indirect github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect - github.com/grafana/grafana-aws-sdk v1.0.4 // indirect + github.com/grafana/grafana-aws-sdk v1.1.0 // indirect github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 // indirect - github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b // indirect + github.com/grafana/grafana/apps/provisioning v0.0.0-20250804150913-990f1c69ecc2 // indirect + github.com/grafana/grafana/pkg/apiserver v0.0.0-20250804150913-990f1c69ecc2 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect - github.com/grafana/sqlds/v4 v4.2.3 // indirect + github.com/grafana/sqlds/v4 v4.2.4 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect diff --git a/apps/advisor/go.sum b/apps/advisor/go.sum index 653ca2c8c71..a20488f5e2d 100644 --- a/apps/advisor/go.sum +++ b/apps/advisor/go.sum @@ -487,15 +487,15 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU= -github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y= github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -667,28 +667,30 @@ github.com/grafana/grafana-app-sdk v0.40.2 h1:j2ftFuqhX+exYUipfEjeWDs3i7oiJkweTF github.com/grafana/grafana-app-sdk v0.40.2/go.mod h1:BbNXPNki3mtbkWxYqJsyA1Cj9AShSyaY33z8WkyfVv0= github.com/grafana/grafana-app-sdk/logging v0.40.1 h1:ru+GqbaQk6jthA5l2Yo1WI/JbNXKNQmLiqNrxz7HGP4= github.com/grafana/grafana-app-sdk/logging v0.40.1/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= -github.com/grafana/grafana-aws-sdk v1.0.4 h1:D14UAehsOqpjliHmHzveRQ1p43KCsMzdmb7GovWj+SY= -github.com/grafana/grafana-aws-sdk v1.0.4/go.mod h1:hO7q7yWV+t6dmiyJjMa3IbuYnYkBua+G/IAlOPVIYKE= +github.com/grafana/grafana-aws-sdk v1.1.0 h1:G0fvwbQmHw14c5RXPd7Gnw9ZQcgzl139LtMDoe0KhmE= +github.com/grafana/grafana-aws-sdk v1.1.0/go.mod h1:7e+47EdHynteYWGoT5Ere9KeOXQObsk8F0vkOLQ1tz8= github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 h1:0TYrkzAc3u0HX+9GK86cGrLTUAcmQfl3/LEB3tL+SOA= github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0/go.mod h1:H9sVh9A4yg5egMGZeh0mifxT1Q/uqwKe1LBjBJU6pN8= github.com/grafana/grafana-plugin-sdk-go v0.278.0 h1:5/rIYparLi02pofdaag8wnjspMMVNCi8cZhC4cdC3Ho= github.com/grafana/grafana-plugin-sdk-go v0.278.0/go.mod h1:+8NXT/XUJ/89GV6FxGQ366NZ3nU+cAXDMd0OUESF9H4= -github.com/grafana/grafana/apps/dashboard v0.0.0-20250730164619-34019e5ec017 h1:Niy+KRDWHsUVqfhZQg0oZbAQFO6QcO6a4l9V/ouDEEs= -github.com/grafana/grafana/apps/dashboard v0.0.0-20250730164619-34019e5ec017/go.mod h1:/iuseD/cEpXDiy7MpL+4qBFZ3H6esnUJTYzpoJMw9dw= -github.com/grafana/grafana/apps/folder v0.0.0-20250627191313-2f1a6ae1712b h1:31MwoIKKT9Ay0ZjbT4lkfcPijiWogUWzXs2EjrCgodI= -github.com/grafana/grafana/apps/folder v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:dLtYBp1pza5HYalezNvzlP8JDeKrZ5BKTonDgEOE0NY= -github.com/grafana/grafana/apps/secret v0.0.0-20250731151929-0aac22a9e2d3 h1:16eaVEucbwis3TxS4CYZxxg5wfPAP/6u7Ji2+wbiHyk= -github.com/grafana/grafana/apps/secret v0.0.0-20250731151929-0aac22a9e2d3/go.mod h1:pS2M5ILsHx9VNTM96glLtCjCVXHWyfGcT34WHvbbMtM= -github.com/grafana/grafana/pkg/aggregator v0.0.0-20250627191313-2f1a6ae1712b h1:ei01IFqmnXkOrrVvsT3CYe+i5xYra3SCX7Wsu3PMsDU= -github.com/grafana/grafana/pkg/aggregator v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:+H4Va9jDJlGQJjAN+OFD/hLx2I/yEzDRMQLaKecvgAc= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250725144121-b1592b5e36d2 h1:lvmcK9XOJUJiYhl2kH4nwAKOUdq+ug+ueIGqfKlip3E= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250725144121-b1592b5e36d2/go.mod h1:3ZgUe0E3rIhI026xF4DKFptOst/jpDHJ/Sn+bRODzI4= -github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b h1:QyJLJn3xwFTIXu9KPZujsrIUN0X8DdiR9b2h75L0AfI= -github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:6OKkPWDB8PetDXqMVMOWL35iTCEUdpATwwpuew0k8+o= +github.com/grafana/grafana/apps/dashboard v0.0.0-20250804150913-990f1c69ecc2 h1:M5kMQVRjcjtnGuw0RM9/iab5VpmJyBwpEDbSfS6IQV4= +github.com/grafana/grafana/apps/dashboard v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:Ik6PLuCNceqTeOesGeQLn4hVL5wCbfscuAEhBEdq8js= +github.com/grafana/grafana/apps/folder v0.0.0-20250804150913-990f1c69ecc2 h1:tubqrwcZaJzS3DM2bLJxK6OCUsmBK0Vs95HiBuhCQcU= +github.com/grafana/grafana/apps/folder v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:la6q+Qvy0JGzBu/yyjC+e7xPefPM7CEfEgD574R2QMU= +github.com/grafana/grafana/apps/provisioning v0.0.0-20250804150913-990f1c69ecc2 h1:83OGBndFNYijNjgQpSf4y1bSNlscVDq8ru97Q6PyU6o= +github.com/grafana/grafana/apps/provisioning v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:qzFUVwLI1b5UIbVFxFydUYAsnOK27AgtA5so3EW8jM0= +github.com/grafana/grafana/apps/secret v0.0.0-20250804150913-990f1c69ecc2 h1:r5oGyvRVxljzKHTQYHtpmK2Et+BVhWQVw7aXo+FUygk= +github.com/grafana/grafana/apps/secret v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:pS2M5ILsHx9VNTM96glLtCjCVXHWyfGcT34WHvbbMtM= +github.com/grafana/grafana/pkg/aggregator v0.0.0-20250804150913-990f1c69ecc2 h1:1AdHE94bYfEG7cuFb92wFlBaVPeexF21l9zCFmN8im0= +github.com/grafana/grafana/pkg/aggregator v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:wU4CoDIHuOOL7ZnchyvfER5UAcEz94A+ZkY+Xt+Ks/8= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 h1:X0cnaFdR+iz+sDSuoZmkryFSjOirchHe2MdKSRwBWgM= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:RRvSjHH12/PnQaXraMO65jUhVu8n59mzvhfIMBETnV4= +github.com/grafana/grafana/pkg/apiserver v0.0.0-20250804150913-990f1c69ecc2 h1:oUINBBiA25EwjOFXJVa4rmU8jHlOGn5EdPCmqpIqy4Q= +github.com/grafana/grafana/pkg/apiserver v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:BA9Rvm6co9tbrZ0lS8ScqLFvAnDQ4lZOyplJgB+yMQQ= github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0= github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao= -github.com/grafana/grafana/pkg/semconv v0.0.0-20250627191313-2f1a6ae1712b h1:m78RNSvTseSpvwQYe5HcdsiADSw4vj6+QUppuCL63gw= -github.com/grafana/grafana/pkg/semconv v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:mu3yl0GxB0eQZV1q7Kka0pkF3Th9x7W04WrjR9wqBlc= +github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 h1:A65jWgLk4Re28gIuZcpC0aTh71JZ0ey89hKGE9h543s= +github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:2HRzUK/xQEYc+8d5If/XSusMcaYq9IptnBSHACiQcOQ= github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= github.com/grafana/prometheus-alertmanager v0.25.1-0.20250620093340-be61a673dee6 h1:oJnbhG6ZNy10AjsgNeAtAKeGHogIGOMfAsBH6fYYa5M= @@ -697,8 +699,8 @@ github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKt github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= -github.com/grafana/sqlds/v4 v4.2.3 h1:9ibD1c5O5u9fifEkBSig+jAc41TUEz+M+bWQqDsofP4= -github.com/grafana/sqlds/v4 v4.2.3/go.mod h1:bv+XHabfUF4xkgg4y+nYFCK8rpMHZsMaQk56qNaJcAM= +github.com/grafana/sqlds/v4 v4.2.4 h1:Xlxy1udWqDK0dlbuJ1qXL7K3EYaf+aKMl38zhd3VbQY= +github.com/grafana/sqlds/v4 v4.2.4/go.mod h1:BQRjUG8rOqrBI4NAaeoWrIMuoNgfi8bdhCJ+5cgEfLU= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= diff --git a/apps/investigations/go.mod b/apps/investigations/go.mod index fe28636873c..d1a3a5f3b6f 100644 --- a/apps/investigations/go.mod +++ b/apps/investigations/go.mod @@ -5,7 +5,7 @@ go 1.24.5 require ( github.com/grafana/grafana v0.0.0-00010101000000-000000000000 github.com/grafana/grafana-app-sdk v0.40.2 - github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250725152715-69d3b9023cec + github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 github.com/stretchr/testify v1.10.0 k8s.io/apimachinery v0.33.3 k8s.io/apiserver v0.33.3 @@ -64,7 +64,7 @@ require ( github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-sql-driver/mysql v1.9.2 // indirect + github.com/go-sql-driver/mysql v1.9.3 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.5 // indirect @@ -84,13 +84,13 @@ require ( github.com/grafana/dataplane/sdata v0.0.9 // indirect github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 // indirect github.com/grafana/grafana-app-sdk/logging v0.40.1 // indirect - github.com/grafana/grafana-aws-sdk v1.0.4 // indirect + github.com/grafana/grafana-aws-sdk v1.1.0 // indirect github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 // indirect github.com/grafana/grafana-plugin-sdk-go v0.278.0 // indirect - github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b // indirect + github.com/grafana/grafana/pkg/apiserver v0.0.0-20250804150913-990f1c69ecc2 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect - github.com/grafana/sqlds/v4 v4.2.3 // indirect + github.com/grafana/sqlds/v4 v4.2.4 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect diff --git a/apps/investigations/go.sum b/apps/investigations/go.sum index 00347e0818c..fdc15812b80 100644 --- a/apps/investigations/go.sum +++ b/apps/investigations/go.sum @@ -243,8 +243,8 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU= -github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= @@ -347,26 +347,28 @@ github.com/grafana/grafana-app-sdk v0.40.2 h1:j2ftFuqhX+exYUipfEjeWDs3i7oiJkweTF github.com/grafana/grafana-app-sdk v0.40.2/go.mod h1:BbNXPNki3mtbkWxYqJsyA1Cj9AShSyaY33z8WkyfVv0= github.com/grafana/grafana-app-sdk/logging v0.40.1 h1:ru+GqbaQk6jthA5l2Yo1WI/JbNXKNQmLiqNrxz7HGP4= github.com/grafana/grafana-app-sdk/logging v0.40.1/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= -github.com/grafana/grafana-aws-sdk v1.0.4 h1:D14UAehsOqpjliHmHzveRQ1p43KCsMzdmb7GovWj+SY= -github.com/grafana/grafana-aws-sdk v1.0.4/go.mod h1:hO7q7yWV+t6dmiyJjMa3IbuYnYkBua+G/IAlOPVIYKE= +github.com/grafana/grafana-aws-sdk v1.1.0 h1:G0fvwbQmHw14c5RXPd7Gnw9ZQcgzl139LtMDoe0KhmE= +github.com/grafana/grafana-aws-sdk v1.1.0/go.mod h1:7e+47EdHynteYWGoT5Ere9KeOXQObsk8F0vkOLQ1tz8= github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 h1:0TYrkzAc3u0HX+9GK86cGrLTUAcmQfl3/LEB3tL+SOA= github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0/go.mod h1:H9sVh9A4yg5egMGZeh0mifxT1Q/uqwKe1LBjBJU6pN8= github.com/grafana/grafana-plugin-sdk-go v0.278.0 h1:5/rIYparLi02pofdaag8wnjspMMVNCi8cZhC4cdC3Ho= github.com/grafana/grafana-plugin-sdk-go v0.278.0/go.mod h1:+8NXT/XUJ/89GV6FxGQ366NZ3nU+cAXDMd0OUESF9H4= -github.com/grafana/grafana/apps/dashboard v0.0.0-20250730164619-34019e5ec017 h1:Niy+KRDWHsUVqfhZQg0oZbAQFO6QcO6a4l9V/ouDEEs= -github.com/grafana/grafana/apps/dashboard v0.0.0-20250730164619-34019e5ec017/go.mod h1:/iuseD/cEpXDiy7MpL+4qBFZ3H6esnUJTYzpoJMw9dw= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250725152715-69d3b9023cec h1:cg1GbDVZ7goqDrqoMzqeN4AeAcD271MGYjOvdVTDwfw= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250725152715-69d3b9023cec/go.mod h1:3ZgUe0E3rIhI026xF4DKFptOst/jpDHJ/Sn+bRODzI4= -github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b h1:QyJLJn3xwFTIXu9KPZujsrIUN0X8DdiR9b2h75L0AfI= -github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:6OKkPWDB8PetDXqMVMOWL35iTCEUdpATwwpuew0k8+o= +github.com/grafana/grafana/apps/dashboard v0.0.0-20250804150913-990f1c69ecc2 h1:M5kMQVRjcjtnGuw0RM9/iab5VpmJyBwpEDbSfS6IQV4= +github.com/grafana/grafana/apps/dashboard v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:Ik6PLuCNceqTeOesGeQLn4hVL5wCbfscuAEhBEdq8js= +github.com/grafana/grafana/apps/provisioning v0.0.0-20250804150913-990f1c69ecc2 h1:83OGBndFNYijNjgQpSf4y1bSNlscVDq8ru97Q6PyU6o= +github.com/grafana/grafana/apps/provisioning v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:qzFUVwLI1b5UIbVFxFydUYAsnOK27AgtA5so3EW8jM0= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 h1:X0cnaFdR+iz+sDSuoZmkryFSjOirchHe2MdKSRwBWgM= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:RRvSjHH12/PnQaXraMO65jUhVu8n59mzvhfIMBETnV4= +github.com/grafana/grafana/pkg/apiserver v0.0.0-20250804150913-990f1c69ecc2 h1:oUINBBiA25EwjOFXJVa4rmU8jHlOGn5EdPCmqpIqy4Q= +github.com/grafana/grafana/pkg/apiserver v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:BA9Rvm6co9tbrZ0lS8ScqLFvAnDQ4lZOyplJgB+yMQQ= github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= github.com/grafana/prometheus-alertmanager v0.25.1-0.20250620093340-be61a673dee6 h1:oJnbhG6ZNy10AjsgNeAtAKeGHogIGOMfAsBH6fYYa5M= github.com/grafana/prometheus-alertmanager v0.25.1-0.20250620093340-be61a673dee6/go.mod h1:O/QP1BCm0HHIzbKvgMzqb5sSyH88rzkFk84F4TfJjBU= github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= -github.com/grafana/sqlds/v4 v4.2.3 h1:9ibD1c5O5u9fifEkBSig+jAc41TUEz+M+bWQqDsofP4= -github.com/grafana/sqlds/v4 v4.2.3/go.mod h1:bv+XHabfUF4xkgg4y+nYFCK8rpMHZsMaQk56qNaJcAM= +github.com/grafana/sqlds/v4 v4.2.4 h1:Xlxy1udWqDK0dlbuJ1qXL7K3EYaf+aKMl38zhd3VbQY= +github.com/grafana/sqlds/v4 v4.2.4/go.mod h1:BQRjUG8rOqrBI4NAaeoWrIMuoNgfi8bdhCJ+5cgEfLU= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= diff --git a/apps/provisioning/Makefile b/apps/provisioning/Makefile new file mode 100644 index 00000000000..c3d7e748603 --- /dev/null +++ b/apps/provisioning/Makefile @@ -0,0 +1,26 @@ +include ../sdk.mk + +.PHONY: generate +generate: install-app-sdk update-app-sdk + @$(APP_SDK_BIN) generate -g ./kinds --grouping=group --postprocess --defencoding=none --useoldmanifestkinds + +.PHONY: build +build: generate + go build -o bin/provisioning ./cmd/operator + +# .PHONY: build/operator +# build/operator: build +# docker build -t provisioning:latest -f cmd/operator/Dockerfile . + +.PHONY: clean +clean: + rm -rf bin/ + rm -rf pkg/generated/ + +.PHONY: test +test: generate + go test ./... + +.PHONY: run +run: build + ./bin/provisioning \ No newline at end of file diff --git a/apps/provisioning/go.mod b/apps/provisioning/go.mod new file mode 100644 index 00000000000..80a98f86697 --- /dev/null +++ b/apps/provisioning/go.mod @@ -0,0 +1,78 @@ +module github.com/grafana/grafana/apps/provisioning + +go 1.24.5 + +require ( + github.com/grafana/grafana-app-sdk v0.40.2 + github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 + k8s.io/apimachinery v0.33.3 + k8s.io/client-go v0.33.3 + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff + sigs.k8s.io/structured-merge-diff/v4 v4.7.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.12.1 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/getkin/kin-openapi v0.132.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-test/deep v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grafana/grafana-app-sdk/logging v0.40.1 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect + github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect + github.com/onsi/ginkgo/v2 v2.22.2 // indirect + github.com/onsi/gomega v1.36.2 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.10.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/term v0.33.0 // indirect + golang.org/x/text v0.27.0 // indirect + golang.org/x/time v0.11.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.33.3 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/yaml v1.5.0 // indirect +) + +// transitive dependencies that need replaced +// TODO: stop depending on grafana core +replace github.com/grafana/grafana => ../.. diff --git a/apps/provisioning/go.sum b/apps/provisioning/go.sum new file mode 100644 index 00000000000..79da45ea597 --- /dev/null +++ b/apps/provisioning/go.sum @@ -0,0 +1,195 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grafana/grafana-app-sdk v0.40.2 h1:j2ftFuqhX+exYUipfEjeWDs3i7oiJkweTF8gFLL7wWU= +github.com/grafana/grafana-app-sdk v0.40.2/go.mod h1:BbNXPNki3mtbkWxYqJsyA1Cj9AShSyaY33z8WkyfVv0= +github.com/grafana/grafana-app-sdk/logging v0.40.1 h1:ru+GqbaQk6jthA5l2Yo1WI/JbNXKNQmLiqNrxz7HGP4= +github.com/grafana/grafana-app-sdk/logging v0.40.1/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 h1:X0cnaFdR+iz+sDSuoZmkryFSjOirchHe2MdKSRwBWgM= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:RRvSjHH12/PnQaXraMO65jUhVu8n59mzvhfIMBETnV4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= +github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= +go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= +golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= +k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= +k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= +k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= +k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ= +sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4= diff --git a/apps/provisioning/kinds/cue.mod/module.cue b/apps/provisioning/kinds/cue.mod/module.cue new file mode 100644 index 00000000000..20a2c78795d --- /dev/null +++ b/apps/provisioning/kinds/cue.mod/module.cue @@ -0,0 +1,4 @@ +module: "github.com/grafana/grafana/apps/provisioning" +language: { + version: "v0.9.0" +} \ No newline at end of file diff --git a/apps/provisioning/kinds/manifest.cue b/apps/provisioning/kinds/manifest.cue new file mode 100644 index 00000000000..40ffa64d922 --- /dev/null +++ b/apps/provisioning/kinds/manifest.cue @@ -0,0 +1,9 @@ +package repository + +manifest: { + appName: "provisioning" + groupOverride: "provisioning.grafana.app" + kinds: [ + repository, + ] +} \ No newline at end of file diff --git a/apps/provisioning/kinds/provisioning/v0alpha1/constants.go b/apps/provisioning/kinds/provisioning/v0alpha1/constants.go new file mode 100644 index 00000000000..c326607a228 --- /dev/null +++ b/apps/provisioning/kinds/provisioning/v0alpha1/constants.go @@ -0,0 +1,18 @@ +package v0alpha1 + +import "k8s.io/apimachinery/pkg/runtime/schema" + +const ( + // APIGroup is the API group used by all kinds in this package + APIGroup = "provisioning.grafana.app" + // APIVersion is the API version used by all kinds in this package + APIVersion = "v0alpha1" +) + +var ( + // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package + GroupVersion = schema.GroupVersion{ + Group: APIGroup, + Version: APIVersion, + } +) diff --git a/apps/provisioning/kinds/provisioning/v0alpha1/repository_codec_gen.go b/apps/provisioning/kinds/provisioning/v0alpha1/repository_codec_gen.go new file mode 100644 index 00000000000..6202f5c6172 --- /dev/null +++ b/apps/provisioning/kinds/provisioning/v0alpha1/repository_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// RepositoryJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type RepositoryJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*RepositoryJSONCodec) Read(reader io.Reader, into resource.Object) error { + return json.NewDecoder(reader).Decode(into) +} + +// Write writes JSON-encoded bytes into `writer` marshaled from `from` +func (*RepositoryJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &RepositoryJSONCodec{} diff --git a/apps/provisioning/kinds/provisioning/v0alpha1/repository_metadata_gen.go b/apps/provisioning/kinds/provisioning/v0alpha1/repository_metadata_gen.go new file mode 100644 index 00000000000..162a34fd13c --- /dev/null +++ b/apps/provisioning/kinds/provisioning/v0alpha1/repository_metadata_gen.go @@ -0,0 +1,31 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type RepositoryMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewRepositoryMetadata creates a new RepositoryMetadata object. +func NewRepositoryMetadata() *RepositoryMetadata { + return &RepositoryMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } +} diff --git a/apps/provisioning/kinds/provisioning/v0alpha1/repository_object_gen.go b/apps/provisioning/kinds/provisioning/v0alpha1/repository_object_gen.go new file mode 100644 index 00000000000..4154ffefb36 --- /dev/null +++ b/apps/provisioning/kinds/provisioning/v0alpha1/repository_object_gen.go @@ -0,0 +1,319 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type Repository struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the Repository + Spec RepositorySpec `json:"spec" yaml:"spec"` + + Status RepositoryStatus `json:"status" yaml:"status"` +} + +func (o *Repository) GetSpec() any { + return o.Spec +} + +func (o *Repository) SetSpec(spec any) error { + cast, ok := spec.(RepositorySpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *Repository) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *Repository) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *Repository) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(RepositoryStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type RepositoryStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *Repository) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *Repository) SetStaticMetadata(metadata resource.StaticMetadata) { + o.Name = metadata.Name + o.Namespace = metadata.Namespace + o.SetGroupVersionKind(schema.GroupVersionKind{ + Group: metadata.Group, + Version: metadata.Version, + Kind: metadata.Kind, + }) +} + +func (o *Repository) GetCommonMetadata() resource.CommonMetadata { + dt := o.DeletionTimestamp + var deletionTimestamp *time.Time + if dt != nil { + deletionTimestamp = &dt.Time + } + // Legacy ExtraFields support + extraFields := make(map[string]any) + if o.Annotations != nil { + extraFields["annotations"] = o.Annotations + } + if o.ManagedFields != nil { + extraFields["managedFields"] = o.ManagedFields + } + if o.OwnerReferences != nil { + extraFields["ownerReferences"] = o.OwnerReferences + } + return resource.CommonMetadata{ + UID: string(o.UID), + ResourceVersion: o.ResourceVersion, + Generation: o.Generation, + Labels: o.Labels, + CreationTimestamp: o.CreationTimestamp.Time, + DeletionTimestamp: deletionTimestamp, + Finalizers: o.Finalizers, + UpdateTimestamp: o.GetUpdateTimestamp(), + CreatedBy: o.GetCreatedBy(), + UpdatedBy: o.GetUpdatedBy(), + ExtraFields: extraFields, + } +} + +func (o *Repository) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *Repository) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *Repository) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *Repository) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *Repository) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *Repository) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *Repository) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *Repository) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *Repository) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *Repository) DeepCopy() *Repository { + cpy := &Repository{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *Repository) DeepCopyInto(dst *Repository) { + dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion + dst.TypeMeta.Kind = o.TypeMeta.Kind + o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta) + o.Spec.DeepCopyInto(&dst.Spec) + o.Status.DeepCopyInto(&dst.Status) +} + +// Interface compliance compile-time check +var _ resource.Object = &Repository{} + +// +k8s:openapi-gen=true +type RepositoryList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []Repository `json:"items" yaml:"items"` +} + +func (o *RepositoryList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *RepositoryList) Copy() resource.ListObject { + cpy := &RepositoryList{ + TypeMeta: o.TypeMeta, + Items: make([]Repository, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*Repository); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *RepositoryList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *RepositoryList) SetItems(items []resource.Object) { + o.Items = make([]Repository, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*Repository) + } +} + +func (o *RepositoryList) DeepCopy() *RepositoryList { + cpy := &RepositoryList{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *RepositoryList) DeepCopyInto(dst *RepositoryList) { + resource.CopyObjectInto(dst, o) +} + +// Interface compliance compile-time check +var _ resource.ListObject = &RepositoryList{} + +// Copy methods for all subresource types + +// DeepCopy creates a full deep copy of Spec +func (s *RepositorySpec) DeepCopy() *RepositorySpec { + cpy := &RepositorySpec{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies Spec into another Spec object +func (s *RepositorySpec) DeepCopyInto(dst *RepositorySpec) { + resource.CopyObjectInto(dst, s) +} + +// DeepCopy creates a full deep copy of RepositoryStatus +func (s *RepositoryStatus) DeepCopy() *RepositoryStatus { + cpy := &RepositoryStatus{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies RepositoryStatus into another RepositoryStatus object +func (s *RepositoryStatus) DeepCopyInto(dst *RepositoryStatus) { + resource.CopyObjectInto(dst, s) +} diff --git a/apps/provisioning/kinds/provisioning/v0alpha1/repository_schema_gen.go b/apps/provisioning/kinds/provisioning/v0alpha1/repository_schema_gen.go new file mode 100644 index 00000000000..e8f3a525d75 --- /dev/null +++ b/apps/provisioning/kinds/provisioning/v0alpha1/repository_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v0alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaRepository = resource.NewSimpleSchema("provisioning.grafana.app", "v0alpha1", &Repository{}, &RepositoryList{}, resource.WithKind("Repository"), + resource.WithPlural("repositories"), resource.WithScope(resource.NamespacedScope)) + kindRepository = resource.Kind{ + Schema: schemaRepository, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &RepositoryJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func RepositoryKind() resource.Kind { + return kindRepository +} + +// Schema returns a resource.SimpleSchema representation of Repository +func RepositorySchema() *resource.SimpleSchema { + return schemaRepository +} + +// Interface compliance checks +var _ resource.Schema = kindRepository diff --git a/apps/provisioning/kinds/provisioning/v0alpha1/repository_spec_gen.go b/apps/provisioning/kinds/provisioning/v0alpha1/repository_spec_gen.go new file mode 100644 index 00000000000..54eb7ba4eda --- /dev/null +++ b/apps/provisioning/kinds/provisioning/v0alpha1/repository_spec_gen.go @@ -0,0 +1,169 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type RepositorySyncOptions struct { + // Enabled must be saved as true before any sync job will run + Enabled bool `json:"enabled"` + // Where values should be saved + Target RepositorySyncOptionsTarget `json:"target"` + // When non-zero, the sync will run periodically + IntervalSeconds *int64 `json:"intervalSeconds,omitempty"` +} + +// NewRepositorySyncOptions creates a new RepositorySyncOptions object. +func NewRepositorySyncOptions() *RepositorySyncOptions { + return &RepositorySyncOptions{} +} + +// +k8s:openapi-gen=true +type RepositoryLocalRepositoryConfig struct { + // Path to the local repository + Path string `json:"path"` +} + +// NewRepositoryLocalRepositoryConfig creates a new RepositoryLocalRepositoryConfig object. +func NewRepositoryLocalRepositoryConfig() *RepositoryLocalRepositoryConfig { + return &RepositoryLocalRepositoryConfig{} +} + +// +k8s:openapi-gen=true +type RepositoryGitHubRepositoryConfig struct { + // The repository URL (e.g. `https://github.com/example/test`). + Url *string `json:"url,omitempty"` + // The branch to use in the repository. + Branch string `json:"branch"` + // Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again. + Token *string `json:"token,omitempty"` + // Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted. + EncryptedToken []string `json:"encryptedToken,omitempty"` + // Whether we should show dashboard previews for pull requests. + // By default, this is false (i.e. we will not create previews). + GenerateDashboardPreviews *bool `json:"generateDashboardPreviews,omitempty"` + // Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. + Path *string `json:"path,omitempty"` +} + +// NewRepositoryGitHubRepositoryConfig creates a new RepositoryGitHubRepositoryConfig object. +func NewRepositoryGitHubRepositoryConfig() *RepositoryGitHubRepositoryConfig { + return &RepositoryGitHubRepositoryConfig{} +} + +// +k8s:openapi-gen=true +type RepositoryGitRepositoryConfig struct { + // The repository URL (e.g. `https://github.com/example/test.git`). + Url *string `json:"url,omitempty"` + // The branch to use in the repository. + Branch string `json:"branch"` + // TokenUser is the user that will be used to access the repository if it's a personal access token. + TokenUser *string `json:"tokenUser,omitempty"` + // Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again. + Token *string `json:"token,omitempty"` + // Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted. + EncryptedToken []string `json:"encryptedToken,omitempty"` + // Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. + Path *string `json:"path,omitempty"` +} + +// NewRepositoryGitRepositoryConfig creates a new RepositoryGitRepositoryConfig object. +func NewRepositoryGitRepositoryConfig() *RepositoryGitRepositoryConfig { + return &RepositoryGitRepositoryConfig{} +} + +// +k8s:openapi-gen=true +type RepositoryBitbucketRepositoryConfig struct { + // The repository URL (e.g. `https://bitbucket.org/example/test`). + Url *string `json:"url,omitempty"` + // The branch to use in the repository. + Branch string `json:"branch"` + // TokenUser is the user that will be used to access the repository if it's a personal access token. + TokenUser *string `json:"tokenUser,omitempty"` + // Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again. + Token *string `json:"token,omitempty"` + // Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted. + EncryptedToken []string `json:"encryptedToken,omitempty"` + // Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. + Path *string `json:"path,omitempty"` +} + +// NewRepositoryBitbucketRepositoryConfig creates a new RepositoryBitbucketRepositoryConfig object. +func NewRepositoryBitbucketRepositoryConfig() *RepositoryBitbucketRepositoryConfig { + return &RepositoryBitbucketRepositoryConfig{} +} + +// +k8s:openapi-gen=true +type RepositoryGitLabRepositoryConfig struct { + // The repository URL (e.g. `https://gitlab.com/example/test`). + Url *string `json:"url,omitempty"` + // The branch to use in the repository. + Branch string `json:"branch"` + // Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again. + Token *string `json:"token,omitempty"` + // Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted. + EncryptedToken []string `json:"encryptedToken,omitempty"` + // Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. + Path *string `json:"path,omitempty"` +} + +// NewRepositoryGitLabRepositoryConfig creates a new RepositoryGitLabRepositoryConfig object. +func NewRepositoryGitLabRepositoryConfig() *RepositoryGitLabRepositoryConfig { + return &RepositoryGitLabRepositoryConfig{} +} + +// +k8s:openapi-gen=true +type RepositorySpec struct { + // The repository display name (shown in the UI) + Title string `json:"title"` + // Repository description + Description *string `json:"description,omitempty"` + // UI driven Workflow that allow changes to the contends of the repository. + // The order is relevant for defining the precedence of the workflows. + // When empty, the repository does not support any edits (eg, readonly) + Workflows []string `json:"workflows,omitempty"` + // Sync settings -- how values are pulled from the repository into grafana + Sync RepositorySyncOptions `json:"sync"` + // The repository type. When selected oneOf the values below should be non-nil + Type RepositorySpecType `json:"type"` + // The repository on the local file system. + // Mutually exclusive with local | github. + Local *RepositoryLocalRepositoryConfig `json:"local,omitempty"` + // The repository on GitHub. + // Mutually exclusive with local | github | git. + Github *RepositoryGitHubRepositoryConfig `json:"github,omitempty"` + // The repository on Git. + // Mutually exclusive with local | github | git. + Git *RepositoryGitRepositoryConfig `json:"git,omitempty"` + // The repository on Bitbucket. + // Mutually exclusive with local | github | git. + Bitbucket *RepositoryBitbucketRepositoryConfig `json:"bitbucket,omitempty"` + // The repository on GitLab. + // Mutually exclusive with local | github | git. + Gitlab *RepositoryGitLabRepositoryConfig `json:"gitlab,omitempty"` +} + +// NewRepositorySpec creates a new RepositorySpec object. +func NewRepositorySpec() *RepositorySpec { + return &RepositorySpec{ + Sync: *NewRepositorySyncOptions(), + } +} + +// +k8s:openapi-gen=true +type RepositorySyncOptionsTarget string + +const ( + RepositorySyncOptionsTargetUnified RepositorySyncOptionsTarget = "unified" + RepositorySyncOptionsTargetLegacy RepositorySyncOptionsTarget = "legacy" +) + +// +k8s:openapi-gen=true +type RepositorySpecType string + +const ( + RepositorySpecTypeLocal RepositorySpecType = "local" + RepositorySpecTypeGithub RepositorySpecType = "github" + RepositorySpecTypeGit RepositorySpecType = "git" + RepositorySpecTypeBitbucket RepositorySpecType = "bitbucket" + RepositorySpecTypeGitlab RepositorySpecType = "gitlab" +) diff --git a/apps/provisioning/kinds/provisioning/v0alpha1/repository_status_gen.go b/apps/provisioning/kinds/provisioning/v0alpha1/repository_status_gen.go new file mode 100644 index 00000000000..c2512cc2851 --- /dev/null +++ b/apps/provisioning/kinds/provisioning/v0alpha1/repository_status_gen.go @@ -0,0 +1,137 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type RepositoryHealthStatus struct { + // When not healthy, requests will not be executed + Healthy bool `json:"healthy"` + // When the health was checked last time + Checked *int64 `json:"checked,omitempty"` + // Summary messages (can be shown to users) + // Will only be populated when not healthy + Message []string `json:"message,omitempty"` +} + +// NewRepositoryHealthStatus creates a new RepositoryHealthStatus object. +func NewRepositoryHealthStatus() *RepositoryHealthStatus { + return &RepositoryHealthStatus{} +} + +// +k8s:openapi-gen=true +type RepositorySyncStatus struct { + // pending, running, success, error + State RepositorySyncStatusState `json:"state"` + // The ID for the job that ran this sync + Job *string `json:"job,omitempty"` + // When the sync job started + Started *int64 `json:"started,omitempty"` + // When the sync job finished + Finished *int64 `json:"finished,omitempty"` + // When the next sync check is scheduled + Scheduled *int64 `json:"scheduled,omitempty"` + // Summary messages (will be shown to users) + Message []string `json:"message"` + // The repository ref when the last successful sync ran + LastRef *string `json:"lastRef,omitempty"` + // Incremental synchronization for versioned repositories + Incremental *bool `json:"incremental,omitempty"` +} + +// NewRepositorySyncStatus creates a new RepositorySyncStatus object. +func NewRepositorySyncStatus() *RepositorySyncStatus { + return &RepositorySyncStatus{ + Message: []string{}, + } +} + +// +k8s:openapi-gen=true +type RepositoryResourceCount struct { + Group string `json:"group"` + Resource string `json:"resource"` + Count int64 `json:"count"` +} + +// NewRepositoryResourceCount creates a new RepositoryResourceCount object. +func NewRepositoryResourceCount() *RepositoryResourceCount { + return &RepositoryResourceCount{} +} + +// +k8s:openapi-gen=true +type RepositorystatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State RepositoryStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewRepositorystatusOperatorState creates a new RepositorystatusOperatorState object. +func NewRepositorystatusOperatorState() *RepositorystatusOperatorState { + return &RepositorystatusOperatorState{} +} + +// +k8s:openapi-gen=true +type RepositoryWebhookStatus struct { + Id *int64 `json:"id,omitempty"` + Url *string `json:"url,omitempty"` + Secret *string `json:"secret,omitempty"` + EncryptedSecret []string `json:"encryptedSecret,omitempty"` + SubscribedEvents []string `json:"subscribedEvents,omitempty"` + LastEvent *int64 `json:"lastEvent,omitempty"` +} + +// NewRepositoryWebhookStatus creates a new RepositoryWebhookStatus object. +func NewRepositoryWebhookStatus() *RepositoryWebhookStatus { + return &RepositoryWebhookStatus{} +} + +// +k8s:openapi-gen=true +type RepositoryStatus struct { + // The generation of the spec last time reconciliation ran + ObservedGeneration *int64 `json:"observedGeneration,omitempty"` + // This will get updated with the current health status (and updated periodically) + Health RepositoryHealthStatus `json:"health"` + // Sync information with the last sync information + Sync RepositorySyncStatus `json:"sync"` + // The object count when sync last ran + Stats []RepositoryResourceCount `json:"stats,omitempty"` + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]RepositorystatusOperatorState `json:"operatorStates,omitempty"` + // Webhook Information (if applicable) + Webhook *RepositoryWebhookStatus `json:"webhook,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewRepositoryStatus creates a new RepositoryStatus object. +func NewRepositoryStatus() *RepositoryStatus { + return &RepositoryStatus{ + Health: *NewRepositoryHealthStatus(), + Sync: *NewRepositorySyncStatus(), + } +} + +// +k8s:openapi-gen=true +type RepositorySyncStatusState string + +const ( + RepositorySyncStatusStatePending RepositorySyncStatusState = "pending" + RepositorySyncStatusStateRunning RepositorySyncStatusState = "running" + RepositorySyncStatusStateSuccess RepositorySyncStatusState = "success" + RepositorySyncStatusStateError RepositorySyncStatusState = "error" +) + +// +k8s:openapi-gen=true +type RepositoryStatusOperatorStateState string + +const ( + RepositoryStatusOperatorStateStateSuccess RepositoryStatusOperatorStateState = "success" + RepositoryStatusOperatorStateStateInProgress RepositoryStatusOperatorStateState = "in_progress" + RepositoryStatusOperatorStateStateFailed RepositoryStatusOperatorStateState = "failed" +) diff --git a/apps/provisioning/kinds/provisioning/v0alpha1/zz_openapi_gen.go b/apps/provisioning/kinds/provisioning/v0alpha1/zz_openapi_gen.go new file mode 100644 index 00000000000..583d9f07f60 --- /dev/null +++ b/apps/provisioning/kinds/provisioning/v0alpha1/zz_openapi_gen.go @@ -0,0 +1,879 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by grafana-app-sdk. DO NOT EDIT. + +package v0alpha1 + +import ( + common "k8s.io/kube-openapi/pkg/common" + spec "k8s.io/kube-openapi/pkg/validation/spec" +) + +func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { + return map[string]common.OpenAPIDefinition{ + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.Repository": schema_provisioning_kinds_provisioning_v0alpha1_Repository(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryBitbucketRepositoryConfig": schema_provisioning_kinds_provisioning_v0alpha1_RepositoryBitbucketRepositoryConfig(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryGitHubRepositoryConfig": schema_provisioning_kinds_provisioning_v0alpha1_RepositoryGitHubRepositoryConfig(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryGitLabRepositoryConfig": schema_provisioning_kinds_provisioning_v0alpha1_RepositoryGitLabRepositoryConfig(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryGitRepositoryConfig": schema_provisioning_kinds_provisioning_v0alpha1_RepositoryGitRepositoryConfig(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryHealthStatus": schema_provisioning_kinds_provisioning_v0alpha1_RepositoryHealthStatus(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryList": schema_provisioning_kinds_provisioning_v0alpha1_RepositoryList(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryLocalRepositoryConfig": schema_provisioning_kinds_provisioning_v0alpha1_RepositoryLocalRepositoryConfig(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryResourceCount": schema_provisioning_kinds_provisioning_v0alpha1_RepositoryResourceCount(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorySpec": schema_provisioning_kinds_provisioning_v0alpha1_RepositorySpec(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryStatus": schema_provisioning_kinds_provisioning_v0alpha1_RepositoryStatus(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorySyncOptions": schema_provisioning_kinds_provisioning_v0alpha1_RepositorySyncOptions(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorySyncStatus": schema_provisioning_kinds_provisioning_v0alpha1_RepositorySyncStatus(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryWebhookStatus": schema_provisioning_kinds_provisioning_v0alpha1_RepositoryWebhookStatus(ref), + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorystatusOperatorState": schema_provisioning_kinds_provisioning_v0alpha1_RepositorystatusOperatorState(ref), + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_Repository(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec is the spec of the Repository", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorySpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryStatus"), + }, + }, + }, + Required: []string{"metadata", "spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorySpec", "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositoryBitbucketRepositoryConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "url": { + SchemaProps: spec.SchemaProps{ + Description: "The repository URL (e.g. `https://bitbucket.org/example/test`).", + Type: []string{"string"}, + Format: "", + }, + }, + "branch": { + SchemaProps: spec.SchemaProps{ + Description: "The branch to use in the repository.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "tokenUser": { + SchemaProps: spec.SchemaProps{ + Description: "TokenUser is the user that will be used to access the repository if it's a personal access token.", + Type: []string{"string"}, + Format: "", + }, + }, + "token": { + SchemaProps: spec.SchemaProps{ + Description: "Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.", + Type: []string{"string"}, + Format: "", + }, + }, + "encryptedToken": { + SchemaProps: spec.SchemaProps{ + Description: "Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"branch"}, + }, + }, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositoryGitHubRepositoryConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "url": { + SchemaProps: spec.SchemaProps{ + Description: "The repository URL (e.g. `https://github.com/example/test`).", + Type: []string{"string"}, + Format: "", + }, + }, + "branch": { + SchemaProps: spec.SchemaProps{ + Description: "The branch to use in the repository.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "token": { + SchemaProps: spec.SchemaProps{ + Description: "Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.", + Type: []string{"string"}, + Format: "", + }, + }, + "encryptedToken": { + SchemaProps: spec.SchemaProps{ + Description: "Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "generateDashboardPreviews": { + SchemaProps: spec.SchemaProps{ + Description: "Whether we should show dashboard previews for pull requests. By default, this is false (i.e. we will not create previews).", + Type: []string{"boolean"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"branch"}, + }, + }, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositoryGitLabRepositoryConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "url": { + SchemaProps: spec.SchemaProps{ + Description: "The repository URL (e.g. `https://gitlab.com/example/test`).", + Type: []string{"string"}, + Format: "", + }, + }, + "branch": { + SchemaProps: spec.SchemaProps{ + Description: "The branch to use in the repository.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "token": { + SchemaProps: spec.SchemaProps{ + Description: "Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.", + Type: []string{"string"}, + Format: "", + }, + }, + "encryptedToken": { + SchemaProps: spec.SchemaProps{ + Description: "Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"branch"}, + }, + }, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositoryGitRepositoryConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "url": { + SchemaProps: spec.SchemaProps{ + Description: "The repository URL (e.g. `https://github.com/example/test.git`).", + Type: []string{"string"}, + Format: "", + }, + }, + "branch": { + SchemaProps: spec.SchemaProps{ + Description: "The branch to use in the repository.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "tokenUser": { + SchemaProps: spec.SchemaProps{ + Description: "TokenUser is the user that will be used to access the repository if it's a personal access token.", + Type: []string{"string"}, + Format: "", + }, + }, + "token": { + SchemaProps: spec.SchemaProps{ + Description: "Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.", + Type: []string{"string"}, + Format: "", + }, + }, + "encryptedToken": { + SchemaProps: spec.SchemaProps{ + Description: "Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"branch"}, + }, + }, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositoryHealthStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "healthy": { + SchemaProps: spec.SchemaProps{ + Description: "When not healthy, requests will not be executed", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "checked": { + SchemaProps: spec.SchemaProps{ + Description: "When the health was checked last time", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Summary messages (can be shown to users) Will only be populated when not healthy", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"healthy"}, + }, + }, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositoryList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.Repository"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.Repository", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositoryLocalRepositoryConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path to the local repository", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"path"}, + }, + }, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositoryResourceCount(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "count": { + SchemaProps: spec.SchemaProps{ + Default: 0, + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + Required: []string{"group", "resource", "count"}, + }, + }, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositorySpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "title": { + SchemaProps: spec.SchemaProps{ + Description: "The repository display name (shown in the UI)", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Repository description", + Type: []string{"string"}, + Format: "", + }, + }, + "workflows": { + SchemaProps: spec.SchemaProps{ + Description: "UI driven Workflow that allow changes to the contends of the repository. The order is relevant for defining the precedence of the workflows. When empty, the repository does not support any edits (eg, readonly)", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "sync": { + SchemaProps: spec.SchemaProps{ + Description: "Sync settings -- how values are pulled from the repository into grafana", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorySyncOptions"), + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "The repository type. When selected oneOf the values below should be non-nil", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "local": { + SchemaProps: spec.SchemaProps{ + Description: "The repository on the local file system. Mutually exclusive with local | github.", + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryLocalRepositoryConfig"), + }, + }, + "github": { + SchemaProps: spec.SchemaProps{ + Description: "The repository on GitHub. Mutually exclusive with local | github | git.", + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryGitHubRepositoryConfig"), + }, + }, + "git": { + SchemaProps: spec.SchemaProps{ + Description: "The repository on Git. Mutually exclusive with local | github | git.", + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryGitRepositoryConfig"), + }, + }, + "bitbucket": { + SchemaProps: spec.SchemaProps{ + Description: "The repository on Bitbucket. Mutually exclusive with local | github | git.", + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryBitbucketRepositoryConfig"), + }, + }, + "gitlab": { + SchemaProps: spec.SchemaProps{ + Description: "The repository on GitLab. Mutually exclusive with local | github | git.", + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryGitLabRepositoryConfig"), + }, + }, + }, + Required: []string{"title", "sync", "type"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryBitbucketRepositoryConfig", "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryGitHubRepositoryConfig", "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryGitLabRepositoryConfig", "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryGitRepositoryConfig", "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryLocalRepositoryConfig", "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorySyncOptions"}, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositoryStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "observedGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "The generation of the spec last time reconciliation ran", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "health": { + SchemaProps: spec.SchemaProps{ + Description: "This will get updated with the current health status (and updated periodically)", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryHealthStatus"), + }, + }, + "sync": { + SchemaProps: spec.SchemaProps{ + Description: "Sync information with the last sync information", + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorySyncStatus"), + }, + }, + "stats": { + SchemaProps: spec.SchemaProps{ + Description: "The object count when sync last ran", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryResourceCount"), + }, + }, + }, + }, + }, + "operatorStates": { + SchemaProps: spec.SchemaProps{ + Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorystatusOperatorState"), + }, + }, + }, + }, + }, + "webhook": { + SchemaProps: spec.SchemaProps{ + Description: "Webhook Information (if applicable)", + Ref: ref("github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryWebhookStatus"), + }, + }, + "additionalFields": { + SchemaProps: spec.SchemaProps{ + Description: "additionalFields is reserved for future use", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"health", "sync"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryHealthStatus", "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryResourceCount", "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorySyncStatus", "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositoryWebhookStatus", "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1.RepositorystatusOperatorState"}, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositorySyncOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "enabled": { + SchemaProps: spec.SchemaProps{ + Description: "Enabled must be saved as true before any sync job will run", + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "target": { + SchemaProps: spec.SchemaProps{ + Description: "Where values should be saved", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "intervalSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "When non-zero, the sync will run periodically", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + Required: []string{"enabled", "target"}, + }, + }, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositorySyncStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "state": { + SchemaProps: spec.SchemaProps{ + Description: "pending, running, success, error", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "job": { + SchemaProps: spec.SchemaProps{ + Description: "The ID for the job that ran this sync", + Type: []string{"string"}, + Format: "", + }, + }, + "started": { + SchemaProps: spec.SchemaProps{ + Description: "When the sync job started", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "finished": { + SchemaProps: spec.SchemaProps{ + Description: "When the sync job finished", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "scheduled": { + SchemaProps: spec.SchemaProps{ + Description: "When the next sync check is scheduled", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Summary messages (will be shown to users)", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "lastRef": { + SchemaProps: spec.SchemaProps{ + Description: "The repository ref when the last successful sync ran", + Type: []string{"string"}, + Format: "", + }, + }, + "incremental": { + SchemaProps: spec.SchemaProps{ + Description: "Incremental synchronization for versioned repositories", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"state", "message"}, + }, + }, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositoryWebhookStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + "url": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "secret": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "encryptedSecret": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "subscribedEvents": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "lastEvent": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + } +} + +func schema_provisioning_kinds_provisioning_v0alpha1_RepositorystatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "lastEvaluation": { + SchemaProps: spec.SchemaProps{ + Description: "lastEvaluation is the ResourceVersion last evaluated", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "descriptiveState": { + SchemaProps: spec.SchemaProps{ + Description: "descriptiveState is an optional more descriptive state field which has no requirements on format", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "details contains any extra information that is operator-specific", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"lastEvaluation", "state"}, + }, + }, + } +} diff --git a/apps/provisioning/kinds/provisioning_manifest.go b/apps/provisioning/kinds/provisioning_manifest.go new file mode 100644 index 00000000000..677a17d8b55 --- /dev/null +++ b/apps/provisioning/kinds/provisioning_manifest.go @@ -0,0 +1,83 @@ +// +// This file is generated by grafana-app-sdk +// DO NOT EDIT +// + +package kinds + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/resource" + + v0alpha1 "github.com/grafana/grafana/apps/provisioning/kinds/provisioning/v0alpha1" +) + +var ( + rawSchemaRepositoryv0alpha1 = []byte(`{"spec":{"properties":{"bitbucket":{"description":"The repository on Bitbucket.\nMutually exclusive with local | github | git.","properties":{"branch":{"description":"The branch to use in the repository.","type":"string"},"encryptedToken":{"description":"Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.","items":{"type":"string"},"type":"array"},"path":{"description":"Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository.","type":"string"},"token":{"description":"Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.","type":"string"},"tokenUser":{"description":"TokenUser is the user that will be used to access the repository if it's a personal access token.","type":"string"},"url":{"description":"The repository URL (e.g. ` + "`" + `https://bitbucket.org/example/test` + "`" + `).","type":"string"}},"required":["branch"],"type":"object"},"description":{"description":"Repository description","type":"string"},"git":{"description":"The repository on Git.\nMutually exclusive with local | github | git.","properties":{"branch":{"description":"The branch to use in the repository.","type":"string"},"encryptedToken":{"description":"Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.","items":{"type":"string"},"type":"array"},"path":{"description":"Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository.","type":"string"},"token":{"description":"Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.","type":"string"},"tokenUser":{"description":"TokenUser is the user that will be used to access the repository if it's a personal access token.","type":"string"},"url":{"description":"The repository URL (e.g. ` + "`" + `https://github.com/example/test.git` + "`" + `).","type":"string"}},"required":["branch"],"type":"object"},"github":{"description":"The repository on GitHub.\nMutually exclusive with local | github | git.","properties":{"branch":{"description":"The branch to use in the repository.","type":"string"},"encryptedToken":{"description":"Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.","items":{"type":"string"},"type":"array"},"generateDashboardPreviews":{"description":"Whether we should show dashboard previews for pull requests.\nBy default, this is false (i.e. we will not create previews).","type":"boolean"},"path":{"description":"Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository.","type":"string"},"token":{"description":"Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.","type":"string"},"url":{"description":"The repository URL (e.g. ` + "`" + `https://github.com/example/test` + "`" + `).","type":"string"}},"required":["branch"],"type":"object"},"gitlab":{"description":"The repository on GitLab.\nMutually exclusive with local | github | git.","properties":{"branch":{"description":"The branch to use in the repository.","type":"string"},"encryptedToken":{"description":"Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.","items":{"type":"string"},"type":"array"},"path":{"description":"Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository.","type":"string"},"token":{"description":"Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.","type":"string"},"url":{"description":"The repository URL (e.g. ` + "`" + `https://gitlab.com/example/test` + "`" + `).","type":"string"}},"required":["branch"],"type":"object"},"local":{"description":"The repository on the local file system.\nMutually exclusive with local | github.","properties":{"path":{"description":"Path to the local repository","type":"string"}},"required":["path"],"type":"object"},"sync":{"description":"Sync settings -- how values are pulled from the repository into grafana","properties":{"enabled":{"description":"Enabled must be saved as true before any sync job will run","type":"boolean"},"intervalSeconds":{"description":"When non-zero, the sync will run periodically","type":"integer"},"target":{"description":"Where values should be saved","enum":["unified","legacy"],"type":"string"}},"required":["enabled","target"],"type":"object"},"title":{"description":"The repository display name (shown in the UI)","type":"string"},"type":{"description":"The repository type. When selected oneOf the values below should be non-nil","enum":["local","github","git","bitbucket","gitlab"],"type":"string"},"workflows":{"description":"UI driven Workflow that allow changes to the contends of the repository.\nThe order is relevant for defining the precedence of the workflows.\nWhen empty, the repository does not support any edits (eg, readonly)","items":{"type":"string"},"type":"array"}},"required":["title","sync","type"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"health":{"description":"This will get updated with the current health status (and updated periodically)","properties":{"checked":{"description":"When the health was checked last time","type":"integer"},"healthy":{"description":"When not healthy, requests will not be executed","type":"boolean"},"message":{"description":"Summary messages (can be shown to users)\nWill only be populated when not healthy","items":{"type":"string"},"type":"array"}},"required":["healthy"],"type":"object"},"observedGeneration":{"description":"The generation of the spec last time reconciliation ran","type":"integer"},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"},"stats":{"description":"The object count when sync last ran","items":{"properties":{"count":{"type":"integer"},"group":{"type":"string"},"resource":{"type":"string"}},"required":["group","resource","count"],"type":"object"},"type":"array"},"sync":{"description":"Sync information with the last sync information","properties":{"finished":{"description":"When the sync job finished","type":"integer"},"incremental":{"description":"Incremental synchronization for versioned repositories","type":"boolean"},"job":{"description":"The ID for the job that ran this sync","type":"string"},"lastRef":{"description":"The repository ref when the last successful sync ran","type":"string"},"message":{"description":"Summary messages (will be shown to users)","items":{"type":"string"},"type":"array"},"scheduled":{"description":"When the next sync check is scheduled","type":"integer"},"started":{"description":"When the sync job started","type":"integer"},"state":{"description":"pending, running, success, error","enum":["pending","running","success","error"],"type":"string"}},"required":["state","message"],"type":"object"},"webhook":{"description":"Webhook Information (if applicable)","properties":{"encryptedSecret":{"items":{"type":"string"},"type":"array"},"id":{"type":"integer"},"lastEvent":{"type":"integer"},"secret":{"type":"string"},"subscribedEvents":{"items":{"type":"string"},"type":"array"},"url":{"type":"string"}},"type":"object"}},"required":["health","sync"],"type":"object"}}`) + versionSchemaRepositoryv0alpha1 app.VersionSchema + _ = json.Unmarshal(rawSchemaRepositoryv0alpha1, &versionSchemaRepositoryv0alpha1) +) + +var appManifestData = app.ManifestData{ + AppName: "provisioning", + Group: "provisioning.grafana.app", + Versions: []app.ManifestVersion{ + { + Name: "v0alpha1", + Served: true, + Kinds: []app.ManifestVersionKind{ + { + Kind: "Repository", + Plural: "Repositories", + Scope: "Namespaced", + Conversion: false, + Admission: &app.AdmissionCapabilities{ + Validation: &app.ValidationCapability{ + Operations: []app.AdmissionOperation{ + app.AdmissionOperationCreate, + app.AdmissionOperationUpdate, + }, + }, + }, + Schema: &versionSchemaRepositoryv0alpha1, + }, + }, + }, + }, +} + +func LocalManifest() app.Manifest { + return app.NewEmbeddedManifest(appManifestData) +} + +func RemoteManifest() app.Manifest { + return app.NewAPIServerManifest("provisioning") +} + +var kindVersionToGoType = map[string]resource.Kind{ + "Repository/v0alpha1": v0alpha1.RepositoryKind(), +} + +// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists. +// If there is no association for the provided Kind and Version, exists will return false. +func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) { + goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)] + return goType, exists +} + +var customRouteToGoResponseType = map[string]any{} + +// ManifestCustomRouteResponsesAssociator returns the associated response go type for a given kind, version, custom route path, and method, if one exists. +// kind may be empty for custom routes which are not kind subroutes. Leading slashes are removed from subroute paths. +// If there is no association for the provided kind, version, custom route path, and method, exists will return false. +func ManifestCustomRouteResponsesAssociator(kind, version, path, verb string) (goType any, exists bool) { + if len(path) > 0 && path[0] == '/' { + path = path[1:] + } + goType, exists = customRouteToGoResponseType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))] + return goType, exists +} diff --git a/apps/provisioning/kinds/repository.cue b/apps/provisioning/kinds/repository.cue new file mode 100644 index 00000000000..ba36b4b962f --- /dev/null +++ b/apps/provisioning/kinds/repository.cue @@ -0,0 +1,171 @@ +package repository + +repository: { + kind: "Repository" + pluralName: "Repositories" + current: "v0alpha1" + validation: { + operations: [ + "CREATE", + "UPDATE", + ] + } + versions: { + "v0alpha1": { + codegen: { + ts: {enabled: false} + go: {enabled: true} + } + schema: { + #LocalRepositoryConfig: { + // Path to the local repository + path: string + } + #GitHubRepositoryConfig: { + // The repository URL (e.g. `https://github.com/example/test`). + url?: string + // The branch to use in the repository. + branch: string + // Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again. + token?: string + // Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted. + encryptedToken?: [...string] + // Whether we should show dashboard previews for pull requests. + // By default, this is false (i.e. we will not create previews). + generateDashboardPreviews?: bool + // Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. + path?: string + } + #GitRepositoryConfig: { + // The repository URL (e.g. `https://github.com/example/test.git`). + url?: string + // The branch to use in the repository. + branch: string + // TokenUser is the user that will be used to access the repository if it's a personal access token. + tokenUser?: string + // Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again. + token?: string + // Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted. + encryptedToken?: [...string] + // Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. + path?: string + } + #BitbucketRepositoryConfig: { + // The repository URL (e.g. `https://bitbucket.org/example/test`). + url?: string + // The branch to use in the repository. + branch: string + // TokenUser is the user that will be used to access the repository if it's a personal access token. + tokenUser?: string + // Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again. + token?: string + // Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted. + encryptedToken?: [...string] + // Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. + path?: string + } + #GitLabRepositoryConfig: { + // The repository URL (e.g. `https://gitlab.com/example/test`). + url?: string + // The branch to use in the repository. + branch: string + // Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again. + token?: string + // Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted. + encryptedToken?: [...string] + // Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. + path?: string + } + #SyncOptions: { + // Enabled must be saved as true before any sync job will run + enabled: bool + // Where values should be saved + target: "unified" | "legacy" + // When non-zero, the sync will run periodically + intervalSeconds?: int + } + #HealthStatus: { + // When not healthy, requests will not be executed + healthy: bool + // When the health was checked last time + checked?: int + // Summary messages (can be shown to users) + // Will only be populated when not healthy + message?: [...string] + } + #SyncStatus: { + // pending, running, success, error + state: "pending" | "running" | "success" | "error" + // The ID for the job that ran this sync + job?: string + // When the sync job started + started?: int + // When the sync job finished + finished?: int + // When the next sync check is scheduled + scheduled?: int + // Summary messages (will be shown to users) + message: [...string] + // The repository ref when the last successful sync ran + lastRef?: string + // Incremental synchronization for versioned repositories + incremental?: bool + } + #ResourceCount: { + group: string + resource: string + count: int + } + #WebhookStatus: { + id?: int + url?: string + secret?: string + encryptedSecret?: [...string] + subscribedEvents?: [...string] + lastEvent?: int + } + spec: { + // The repository display name (shown in the UI) + title: string + // Repository description + description?: string + // UI driven Workflow that allow changes to the contends of the repository. + // The order is relevant for defining the precedence of the workflows. + // When empty, the repository does not support any edits (eg, readonly) + workflows?: [...string] + // Sync settings -- how values are pulled from the repository into grafana + sync: #SyncOptions + // The repository type. When selected oneOf the values below should be non-nil + type: "local" | "github" | "git" | "bitbucket" | "gitlab" + // The repository on the local file system. + // Mutually exclusive with local | github. + local?: #LocalRepositoryConfig + // The repository on GitHub. + // Mutually exclusive with local | github | git. + github?: #GitHubRepositoryConfig + // The repository on Git. + // Mutually exclusive with local | github | git. + git?: #GitRepositoryConfig + // The repository on Bitbucket. + // Mutually exclusive with local | github | git. + bitbucket?: #BitbucketRepositoryConfig + // The repository on GitLab. + // Mutually exclusive with local | github | git. + gitlab?: #GitLabRepositoryConfig + } + status: { + // The generation of the spec last time reconciliation ran + observedGeneration?: int + // This will get updated with the current health status (and updated periodically) + health: #HealthStatus + // Sync information with the last sync information + sync: #SyncStatus + // The object count when sync last ran + stats?: [...#ResourceCount] + // Webhook Information (if applicable) + webhook?: #WebhookStatus + } + } + } + } +} \ No newline at end of file diff --git a/pkg/apis/provisioning/v0alpha1/classic.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/classic.go similarity index 100% rename from pkg/apis/provisioning/v0alpha1/classic.go rename to apps/provisioning/pkg/apis/provisioning/v0alpha1/classic.go diff --git a/pkg/apis/provisioning/v0alpha1/doc.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/doc.go similarity index 54% rename from pkg/apis/provisioning/v0alpha1/doc.go rename to apps/provisioning/pkg/apis/provisioning/v0alpha1/doc.go index f24a3d11d11..4499de75b13 100644 --- a/pkg/apis/provisioning/v0alpha1/doc.go +++ b/apps/provisioning/pkg/apis/provisioning/v0alpha1/doc.go @@ -3,4 +3,4 @@ // +k8s:defaulter-gen=TypeMeta // +groupName=provisioning.grafana.app -package v0alpha1 // import "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" +package v0alpha1 // import "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" diff --git a/pkg/apis/provisioning/v0alpha1/jobs.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/jobs.go similarity index 100% rename from pkg/apis/provisioning/v0alpha1/jobs.go rename to apps/provisioning/pkg/apis/provisioning/v0alpha1/jobs.go diff --git a/pkg/apis/provisioning/v0alpha1/register.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/register.go similarity index 100% rename from pkg/apis/provisioning/v0alpha1/register.go rename to apps/provisioning/pkg/apis/provisioning/v0alpha1/register.go diff --git a/pkg/apis/provisioning/v0alpha1/settings.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/settings.go similarity index 100% rename from pkg/apis/provisioning/v0alpha1/settings.go rename to apps/provisioning/pkg/apis/provisioning/v0alpha1/settings.go diff --git a/pkg/apis/provisioning/v0alpha1/types.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/types.go similarity index 100% rename from pkg/apis/provisioning/v0alpha1/types.go rename to apps/provisioning/pkg/apis/provisioning/v0alpha1/types.go diff --git a/pkg/apis/provisioning/v0alpha1/types_test.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/types_test.go similarity index 91% rename from pkg/apis/provisioning/v0alpha1/types_test.go rename to apps/provisioning/pkg/apis/provisioning/v0alpha1/types_test.go index ef8643e2a1e..82421b7d611 100644 --- a/pkg/apis/provisioning/v0alpha1/types_test.go +++ b/apps/provisioning/pkg/apis/provisioning/v0alpha1/types_test.go @@ -3,7 +3,7 @@ package v0alpha1_test import ( "testing" - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) func TestRepositoryType_IsGit(t *testing.T) { diff --git a/pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go similarity index 100% rename from pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go rename to apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.deepcopy.go diff --git a/pkg/apis/provisioning/v0alpha1/zz_generated.defaults.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.defaults.go similarity index 100% rename from pkg/apis/provisioning/v0alpha1/zz_generated.defaults.go rename to apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.defaults.go diff --git a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go similarity index 82% rename from pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go rename to apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go index e6e9e4e9ab5..19911653735 100644 --- a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go +++ b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi.go @@ -14,53 +14,53 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Author": schema_pkg_apis_provisioning_v0alpha1_Author(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.BitbucketRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_BitbucketRepositoryConfig(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.DeleteJobOptions": schema_pkg_apis_provisioning_v0alpha1_DeleteJobOptions(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ErrorDetails": schema_pkg_apis_provisioning_v0alpha1_ErrorDetails(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ExportJobOptions": schema_pkg_apis_provisioning_v0alpha1_ExportJobOptions(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.FileItem": schema_pkg_apis_provisioning_v0alpha1_FileItem(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.FileList": schema_pkg_apis_provisioning_v0alpha1_FileList(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.GitHubRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_GitHubRepositoryConfig(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.GitLabRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_GitLabRepositoryConfig(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.GitRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_GitRepositoryConfig(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.HealthStatus": schema_pkg_apis_provisioning_v0alpha1_HealthStatus(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.HistoryItem": schema_pkg_apis_provisioning_v0alpha1_HistoryItem(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.HistoryList": schema_pkg_apis_provisioning_v0alpha1_HistoryList(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Job": schema_pkg_apis_provisioning_v0alpha1_Job(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobList": schema_pkg_apis_provisioning_v0alpha1_JobList(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobResourceSummary": schema_pkg_apis_provisioning_v0alpha1_JobResourceSummary(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobSpec": schema_pkg_apis_provisioning_v0alpha1_JobSpec(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobStatus": schema_pkg_apis_provisioning_v0alpha1_JobStatus(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.LocalRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_LocalRepositoryConfig(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ManagerStats": schema_pkg_apis_provisioning_v0alpha1_ManagerStats(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.MigrateJobOptions": schema_pkg_apis_provisioning_v0alpha1_MigrateJobOptions(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.MoveJobOptions": schema_pkg_apis_provisioning_v0alpha1_MoveJobOptions(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.PullRequestJobOptions": schema_pkg_apis_provisioning_v0alpha1_PullRequestJobOptions(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RefItem": schema_pkg_apis_provisioning_v0alpha1_RefItem(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RefList": schema_pkg_apis_provisioning_v0alpha1_RefList(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Repository": schema_pkg_apis_provisioning_v0alpha1_Repository(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositoryList": schema_pkg_apis_provisioning_v0alpha1_RepositoryList(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositorySpec": schema_pkg_apis_provisioning_v0alpha1_RepositorySpec(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositoryStatus": schema_pkg_apis_provisioning_v0alpha1_RepositoryStatus(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositoryView": schema_pkg_apis_provisioning_v0alpha1_RepositoryView(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositoryViewList": schema_pkg_apis_provisioning_v0alpha1_RepositoryViewList(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceCount": schema_pkg_apis_provisioning_v0alpha1_ResourceCount(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceList": schema_pkg_apis_provisioning_v0alpha1_ResourceList(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceListItem": schema_pkg_apis_provisioning_v0alpha1_ResourceListItem(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceObjects": schema_pkg_apis_provisioning_v0alpha1_ResourceObjects(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceRef": schema_pkg_apis_provisioning_v0alpha1_ResourceRef(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceRepositoryInfo": schema_pkg_apis_provisioning_v0alpha1_ResourceRepositoryInfo(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceStats": schema_pkg_apis_provisioning_v0alpha1_ResourceStats(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceType": schema_pkg_apis_provisioning_v0alpha1_ResourceType(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceURLs": schema_pkg_apis_provisioning_v0alpha1_ResourceURLs(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceWrapper": schema_pkg_apis_provisioning_v0alpha1_ResourceWrapper(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.SyncJobOptions": schema_pkg_apis_provisioning_v0alpha1_SyncJobOptions(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.SyncOptions": schema_pkg_apis_provisioning_v0alpha1_SyncOptions(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.SyncStatus": schema_pkg_apis_provisioning_v0alpha1_SyncStatus(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.TestResults": schema_pkg_apis_provisioning_v0alpha1_TestResults(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.WebhookResponse": schema_pkg_apis_provisioning_v0alpha1_WebhookResponse(ref), - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.WebhookStatus": schema_pkg_apis_provisioning_v0alpha1_WebhookStatus(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.Author": schema_pkg_apis_provisioning_v0alpha1_Author(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.BitbucketRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_BitbucketRepositoryConfig(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.DeleteJobOptions": schema_pkg_apis_provisioning_v0alpha1_DeleteJobOptions(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ErrorDetails": schema_pkg_apis_provisioning_v0alpha1_ErrorDetails(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ExportJobOptions": schema_pkg_apis_provisioning_v0alpha1_ExportJobOptions(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.FileItem": schema_pkg_apis_provisioning_v0alpha1_FileItem(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.FileList": schema_pkg_apis_provisioning_v0alpha1_FileList(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.GitHubRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_GitHubRepositoryConfig(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.GitLabRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_GitLabRepositoryConfig(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.GitRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_GitRepositoryConfig(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HealthStatus": schema_pkg_apis_provisioning_v0alpha1_HealthStatus(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HistoryItem": schema_pkg_apis_provisioning_v0alpha1_HistoryItem(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HistoryList": schema_pkg_apis_provisioning_v0alpha1_HistoryList(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.Job": schema_pkg_apis_provisioning_v0alpha1_Job(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobList": schema_pkg_apis_provisioning_v0alpha1_JobList(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobResourceSummary": schema_pkg_apis_provisioning_v0alpha1_JobResourceSummary(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobSpec": schema_pkg_apis_provisioning_v0alpha1_JobSpec(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobStatus": schema_pkg_apis_provisioning_v0alpha1_JobStatus(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.LocalRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_LocalRepositoryConfig(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ManagerStats": schema_pkg_apis_provisioning_v0alpha1_ManagerStats(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.MigrateJobOptions": schema_pkg_apis_provisioning_v0alpha1_MigrateJobOptions(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.MoveJobOptions": schema_pkg_apis_provisioning_v0alpha1_MoveJobOptions(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.PullRequestJobOptions": schema_pkg_apis_provisioning_v0alpha1_PullRequestJobOptions(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RefItem": schema_pkg_apis_provisioning_v0alpha1_RefItem(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RefList": schema_pkg_apis_provisioning_v0alpha1_RefList(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.Repository": schema_pkg_apis_provisioning_v0alpha1_Repository(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositoryList": schema_pkg_apis_provisioning_v0alpha1_RepositoryList(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositorySpec": schema_pkg_apis_provisioning_v0alpha1_RepositorySpec(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositoryStatus": schema_pkg_apis_provisioning_v0alpha1_RepositoryStatus(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositoryView": schema_pkg_apis_provisioning_v0alpha1_RepositoryView(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositoryViewList": schema_pkg_apis_provisioning_v0alpha1_RepositoryViewList(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceCount": schema_pkg_apis_provisioning_v0alpha1_ResourceCount(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceList": schema_pkg_apis_provisioning_v0alpha1_ResourceList(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceListItem": schema_pkg_apis_provisioning_v0alpha1_ResourceListItem(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceObjects": schema_pkg_apis_provisioning_v0alpha1_ResourceObjects(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceRef": schema_pkg_apis_provisioning_v0alpha1_ResourceRef(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceRepositoryInfo": schema_pkg_apis_provisioning_v0alpha1_ResourceRepositoryInfo(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceStats": schema_pkg_apis_provisioning_v0alpha1_ResourceStats(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceType": schema_pkg_apis_provisioning_v0alpha1_ResourceType(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceURLs": schema_pkg_apis_provisioning_v0alpha1_ResourceURLs(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceWrapper": schema_pkg_apis_provisioning_v0alpha1_ResourceWrapper(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncJobOptions": schema_pkg_apis_provisioning_v0alpha1_SyncJobOptions(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncOptions": schema_pkg_apis_provisioning_v0alpha1_SyncOptions(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncStatus": schema_pkg_apis_provisioning_v0alpha1_SyncStatus(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.TestResults": schema_pkg_apis_provisioning_v0alpha1_TestResults(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.WebhookResponse": schema_pkg_apis_provisioning_v0alpha1_WebhookResponse(ref), + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.WebhookStatus": schema_pkg_apis_provisioning_v0alpha1_WebhookStatus(ref), } } @@ -194,7 +194,7 @@ func schema_pkg_apis_provisioning_v0alpha1_DeleteJobOptions(ref common.Reference Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceRef"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceRef"), }, }, }, @@ -204,7 +204,7 @@ func schema_pkg_apis_provisioning_v0alpha1_DeleteJobOptions(ref common.Reference }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceRef"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceRef"}, } } @@ -363,7 +363,7 @@ func schema_pkg_apis_provisioning_v0alpha1_FileList(ref common.ReferenceCallback Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.FileItem"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.FileItem"), }, }, }, @@ -374,7 +374,7 @@ func schema_pkg_apis_provisioning_v0alpha1_FileList(ref common.ReferenceCallback }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.FileItem", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.FileItem", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, } } @@ -634,7 +634,7 @@ func schema_pkg_apis_provisioning_v0alpha1_HistoryItem(ref common.ReferenceCallb Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Author"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.Author"), }, }, }, @@ -652,7 +652,7 @@ func schema_pkg_apis_provisioning_v0alpha1_HistoryItem(ref common.ReferenceCallb }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Author"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.Author"}, } } @@ -695,7 +695,7 @@ func schema_pkg_apis_provisioning_v0alpha1_HistoryList(ref common.ReferenceCallb Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.HistoryItem"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HistoryItem"), }, }, }, @@ -706,7 +706,7 @@ func schema_pkg_apis_provisioning_v0alpha1_HistoryList(ref common.ReferenceCallb }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.HistoryItem", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HistoryItem", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, } } @@ -740,20 +740,20 @@ func schema_pkg_apis_provisioning_v0alpha1_Job(ref common.ReferenceCallback) com "spec": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobSpec"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobSpec"), }, }, "status": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobStatus"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobStatus"), }, }, }, }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobSpec", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobSpec", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } @@ -790,7 +790,7 @@ func schema_pkg_apis_provisioning_v0alpha1_JobList(ref common.ReferenceCallback) Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Job"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.Job"), }, }, }, @@ -801,7 +801,7 @@ func schema_pkg_apis_provisioning_v0alpha1_JobList(ref common.ReferenceCallback) }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Job", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.Job", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, } } @@ -912,44 +912,44 @@ func schema_pkg_apis_provisioning_v0alpha1_JobSpec(ref common.ReferenceCallback) "pr": { SchemaProps: spec.SchemaProps{ Description: "Pull request options", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.PullRequestJobOptions"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.PullRequestJobOptions"), }, }, "push": { SchemaProps: spec.SchemaProps{ Description: "Required when the action is `push`", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ExportJobOptions"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ExportJobOptions"), }, }, "pull": { SchemaProps: spec.SchemaProps{ Description: "Required when the action is `pull`", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.SyncJobOptions"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncJobOptions"), }, }, "migrate": { SchemaProps: spec.SchemaProps{ Description: "Required when the action is `migrate`", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.MigrateJobOptions"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.MigrateJobOptions"), }, }, "delete": { SchemaProps: spec.SchemaProps{ Description: "Delete when the action is `delete`", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.DeleteJobOptions"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.DeleteJobOptions"), }, }, "move": { SchemaProps: spec.SchemaProps{ Description: "Move when the action is `move`", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.MoveJobOptions"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.MoveJobOptions"), }, }, }, }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.DeleteJobOptions", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ExportJobOptions", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.MigrateJobOptions", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.MoveJobOptions", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.PullRequestJobOptions", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.SyncJobOptions"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.DeleteJobOptions", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ExportJobOptions", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.MigrateJobOptions", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.MoveJobOptions", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.PullRequestJobOptions", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncJobOptions"}, } } @@ -1014,7 +1014,7 @@ func schema_pkg_apis_provisioning_v0alpha1_JobStatus(ref common.ReferenceCallbac Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobResourceSummary"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobResourceSummary"), }, }, }, @@ -1024,7 +1024,7 @@ func schema_pkg_apis_provisioning_v0alpha1_JobStatus(ref common.ReferenceCallbac }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobResourceSummary"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobResourceSummary"}, } } @@ -1074,7 +1074,7 @@ func schema_pkg_apis_provisioning_v0alpha1_ManagerStats(ref common.ReferenceCall Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceCount"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceCount"), }, }, }, @@ -1085,7 +1085,7 @@ func schema_pkg_apis_provisioning_v0alpha1_ManagerStats(ref common.ReferenceCall }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceCount"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceCount"}, } } @@ -1158,7 +1158,7 @@ func schema_pkg_apis_provisioning_v0alpha1_MoveJobOptions(ref common.ReferenceCa Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceRef"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceRef"), }, }, }, @@ -1168,7 +1168,7 @@ func schema_pkg_apis_provisioning_v0alpha1_MoveJobOptions(ref common.ReferenceCa }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceRef"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceRef"}, } } @@ -1285,7 +1285,7 @@ func schema_pkg_apis_provisioning_v0alpha1_RefList(ref common.ReferenceCallback) Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RefItem"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RefItem"), }, }, }, @@ -1296,7 +1296,7 @@ func schema_pkg_apis_provisioning_v0alpha1_RefList(ref common.ReferenceCallback) }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RefItem", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RefItem", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, } } @@ -1330,20 +1330,20 @@ func schema_pkg_apis_provisioning_v0alpha1_Repository(ref common.ReferenceCallba "spec": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositorySpec"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositorySpec"), }, }, "status": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositoryStatus"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositoryStatus"), }, }, }, }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositorySpec", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositoryStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositorySpec", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositoryStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } @@ -1385,7 +1385,7 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositoryList(ref common.ReferenceCa Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Repository"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.Repository"), }, }, }, @@ -1396,7 +1396,7 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositoryList(ref common.ReferenceCa }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Repository", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.Repository", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, } } @@ -1441,7 +1441,7 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositorySpec(ref common.ReferenceCa SchemaProps: spec.SchemaProps{ Description: "Sync settings -- how values are pulled from the repository into grafana", Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.SyncOptions"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncOptions"), }, }, "type": { @@ -1456,31 +1456,31 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositorySpec(ref common.ReferenceCa "local": { SchemaProps: spec.SchemaProps{ Description: "The repository on the local file system. Mutually exclusive with local | github.", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.LocalRepositoryConfig"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.LocalRepositoryConfig"), }, }, "github": { SchemaProps: spec.SchemaProps{ Description: "The repository on GitHub. Mutually exclusive with local | github | git.", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.GitHubRepositoryConfig"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.GitHubRepositoryConfig"), }, }, "git": { SchemaProps: spec.SchemaProps{ Description: "The repository on Git. Mutually exclusive with local | github | git.", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.GitRepositoryConfig"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.GitRepositoryConfig"), }, }, "bitbucket": { SchemaProps: spec.SchemaProps{ Description: "The repository on Bitbucket. Mutually exclusive with local | github | git.", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.BitbucketRepositoryConfig"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.BitbucketRepositoryConfig"), }, }, "gitlab": { SchemaProps: spec.SchemaProps{ Description: "The repository on GitLab. Mutually exclusive with local | github | git.", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.GitLabRepositoryConfig"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.GitLabRepositoryConfig"), }, }, }, @@ -1488,7 +1488,7 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositorySpec(ref common.ReferenceCa }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.BitbucketRepositoryConfig", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.GitHubRepositoryConfig", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.GitLabRepositoryConfig", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.GitRepositoryConfig", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.LocalRepositoryConfig", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.SyncOptions"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.BitbucketRepositoryConfig", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.GitHubRepositoryConfig", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.GitLabRepositoryConfig", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.GitRepositoryConfig", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.LocalRepositoryConfig", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncOptions"}, } } @@ -1511,14 +1511,14 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositoryStatus(ref common.Reference SchemaProps: spec.SchemaProps{ Description: "This will get updated with the current health status (and updated periodically)", Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.HealthStatus"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HealthStatus"), }, }, "sync": { SchemaProps: spec.SchemaProps{ Description: "Sync information with the last sync information", Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.SyncStatus"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncStatus"), }, }, "stats": { @@ -1534,7 +1534,7 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositoryStatus(ref common.Reference Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceCount"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceCount"), }, }, }, @@ -1543,7 +1543,7 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositoryStatus(ref common.Reference "webhook": { SchemaProps: spec.SchemaProps{ Description: "Webhook Information (if applicable)", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.WebhookStatus"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.WebhookStatus"), }, }, }, @@ -1551,7 +1551,7 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositoryStatus(ref common.Reference }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.HealthStatus", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceCount", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.SyncStatus", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.WebhookStatus"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.HealthStatus", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceCount", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.SyncStatus", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.WebhookStatus"}, } } @@ -1681,7 +1681,7 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositoryViewList(ref common.Referen Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositoryView"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositoryView"), }, }, }, @@ -1692,7 +1692,7 @@ func schema_pkg_apis_provisioning_v0alpha1_RepositoryViewList(ref common.Referen }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositoryView"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.RepositoryView"}, } } @@ -1769,7 +1769,7 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceList(ref common.ReferenceCall Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceListItem"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceListItem"), }, }, }, @@ -1780,7 +1780,7 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceList(ref common.ReferenceCall }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceListItem", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceListItem", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, } } @@ -1861,7 +1861,7 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceObjects(ref common.ReferenceC SchemaProps: spec.SchemaProps{ Description: "The identified type for this object", Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceType"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceType"), }, }, "file": { @@ -1901,7 +1901,7 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceObjects(ref common.ReferenceC }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceType"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceType", "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"}, } } @@ -2024,7 +2024,7 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceStats(ref common.ReferenceCal Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceCount"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceCount"), }, }, }, @@ -2043,7 +2043,7 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceStats(ref common.ReferenceCal Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ManagerStats"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ManagerStats"), }, }, }, @@ -2053,7 +2053,7 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceStats(ref common.ReferenceCal }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ManagerStats", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceCount", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ManagerStats", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceCount", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, } } @@ -2187,13 +2187,13 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceWrapper(ref common.ReferenceC SchemaProps: spec.SchemaProps{ Description: "Basic repository info", Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceRepositoryInfo"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceRepositoryInfo"), }, }, "urls": { SchemaProps: spec.SchemaProps{ Description: "Typed links for this file (only supported by external systems, github etc)", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceURLs"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceURLs"), }, }, "timestamp": { @@ -2206,7 +2206,7 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceWrapper(ref common.ReferenceC SchemaProps: spec.SchemaProps{ Description: "Different flavors of the same object", Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceObjects"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceObjects"), }, }, "errors": { @@ -2234,7 +2234,7 @@ func schema_pkg_apis_provisioning_v0alpha1_ResourceWrapper(ref common.ReferenceC }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceObjects", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceRepositoryInfo", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ResourceURLs", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceObjects", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceRepositoryInfo", "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ResourceURLs", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } @@ -2425,7 +2425,7 @@ func schema_pkg_apis_provisioning_v0alpha1_TestResults(ref common.ReferenceCallb Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ErrorDetails"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ErrorDetails"), }, }, }, @@ -2436,7 +2436,7 @@ func schema_pkg_apis_provisioning_v0alpha1_TestResults(ref common.ReferenceCallb }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ErrorDetails"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.ErrorDetails"}, } } @@ -2477,14 +2477,14 @@ func schema_pkg_apis_provisioning_v0alpha1_WebhookResponse(ref common.ReferenceC "job": { SchemaProps: spec.SchemaProps{ Description: "Jobs to be processed When the response is 202 (Accepted) the queued jobs will be returned", - Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobSpec"), + Ref: ref("github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobSpec"), }, }, }, }, }, Dependencies: []string{ - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobSpec"}, + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1.JobSpec"}, } } diff --git a/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list new file mode 100644 index 00000000000..a92f95837a2 --- /dev/null +++ b/apps/provisioning/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list @@ -0,0 +1,26 @@ +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,DeleteJobOptions,Paths +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,DeleteJobOptions,Resources +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,FileList,Items +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,HistoryList,Items +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,JobResourceSummary,Errors +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,JobStatus,Errors +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,JobStatus,Summary +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,ManagerStats,Stats +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,MoveJobOptions,Paths +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,MoveJobOptions,Resources +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,RefList,Items +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,RepositoryList,Items +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,RepositorySpec,Workflows +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,RepositoryView,Workflows +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,RepositoryViewList,AvailableRepositoryTypes +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,RepositoryViewList,Items +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,ResourceList,Items +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,TestResults,Errors +API rule violation: list_type_missing,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,WebhookStatus,SubscribedEvents +API rule violation: names_match,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,JobSpec,PullRequest +API rule violation: names_match,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,ManagerStats,Identity +API rule violation: names_match,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,RepositorySpec,GitHub +API rule violation: names_match,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,RepositorySpec,GitLab +API rule violation: names_match,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,ResourceWrapper,URLs +API rule violation: names_match,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,SyncStatus,JobID +API rule violation: names_match,github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1,WebhookResponse,Message diff --git a/apps/provisioning/pkg/generated/applyconfiguration/internal/internal.go b/apps/provisioning/pkg/generated/applyconfiguration/internal/internal.go new file mode 100644 index 00000000000..ddd9f734c85 --- /dev/null +++ b/apps/provisioning/pkg/generated/applyconfiguration/internal/internal.go @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package internal + +import ( + fmt "fmt" + sync "sync" + + typed "sigs.k8s.io/structured-merge-diff/v4/typed" +) + +func Parser() *typed.Parser { + parserOnce.Do(func() { + var err error + parser, err = typed.NewParser(schemaYAML) + if err != nil { + panic(fmt.Sprintf("Failed to parse schema: %v", err)) + } + }) + return parser +} + +var parserOnce sync.Once +var parser *typed.Parser +var schemaYAML = typed.YAMLObject(`types: +- name: __untyped_atomic_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: __untyped_deduced_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable +`) diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/bitbucketrepositoryconfig.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/bitbucketrepositoryconfig.go similarity index 100% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/bitbucketrepositoryconfig.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/bitbucketrepositoryconfig.go diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/githubrepositoryconfig.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/githubrepositoryconfig.go similarity index 100% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/githubrepositoryconfig.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/githubrepositoryconfig.go diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/gitlabrepositoryconfig.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/gitlabrepositoryconfig.go similarity index 100% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/gitlabrepositoryconfig.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/gitlabrepositoryconfig.go diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/gitrepositoryconfig.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/gitrepositoryconfig.go similarity index 100% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/gitrepositoryconfig.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/gitrepositoryconfig.go diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/healthstatus.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/healthstatus.go similarity index 100% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/healthstatus.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/healthstatus.go diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/localrepositoryconfig.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/localrepositoryconfig.go similarity index 100% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/localrepositoryconfig.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/localrepositoryconfig.go diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/repository.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/repository.go similarity index 100% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/repository.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/repository.go diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositoryspec.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositoryspec.go similarity index 98% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/repositoryspec.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositoryspec.go index 13d9e45d582..6fff6f2de42 100644 --- a/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositoryspec.go +++ b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositoryspec.go @@ -5,7 +5,7 @@ package v0alpha1 import ( - provisioningv0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // RepositorySpecApplyConfiguration represents a declarative configuration of the RepositorySpec type for use diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositorystatus.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositorystatus.go similarity index 100% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/repositorystatus.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/repositorystatus.go diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/resourcecount.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/resourcecount.go similarity index 100% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/resourcecount.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/resourcecount.go diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/syncoptions.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/syncoptions.go similarity index 95% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/syncoptions.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/syncoptions.go index 837b071930f..05e7396e536 100644 --- a/pkg/generated/applyconfiguration/provisioning/v0alpha1/syncoptions.go +++ b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/syncoptions.go @@ -5,7 +5,7 @@ package v0alpha1 import ( - provisioningv0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // SyncOptionsApplyConfiguration represents a declarative configuration of the SyncOptions type for use diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/syncstatus.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/syncstatus.go similarity index 97% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/syncstatus.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/syncstatus.go index 6b8f5abbac1..408e452f9a1 100644 --- a/pkg/generated/applyconfiguration/provisioning/v0alpha1/syncstatus.go +++ b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/syncstatus.go @@ -5,7 +5,7 @@ package v0alpha1 import ( - provisioningv0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // SyncStatusApplyConfiguration represents a declarative configuration of the SyncStatus type for use diff --git a/pkg/generated/applyconfiguration/provisioning/v0alpha1/webhookstatus.go b/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/webhookstatus.go similarity index 100% rename from pkg/generated/applyconfiguration/provisioning/v0alpha1/webhookstatus.go rename to apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1/webhookstatus.go diff --git a/apps/provisioning/pkg/generated/applyconfiguration/utils.go b/apps/provisioning/pkg/generated/applyconfiguration/utils.go new file mode 100644 index 00000000000..29392ef2f2a --- /dev/null +++ b/apps/provisioning/pkg/generated/applyconfiguration/utils.go @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package applyconfiguration + +import ( + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + internal "github.com/grafana/grafana/apps/provisioning/pkg/generated/applyconfiguration/internal" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + testing "k8s.io/client-go/testing" +) + +// ForKind returns an apply configuration type for the given GroupVersionKind, or nil if no +// apply configuration type exists for the given GroupVersionKind. +func ForKind(kind schema.GroupVersionKind) interface{} { + switch kind { + // Group=provisioning.grafana.app, Version=v0alpha1 + case v0alpha1.SchemeGroupVersion.WithKind("BitbucketRepositoryConfig"): + return &provisioningv0alpha1.BitbucketRepositoryConfigApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("GitHubRepositoryConfig"): + return &provisioningv0alpha1.GitHubRepositoryConfigApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("GitLabRepositoryConfig"): + return &provisioningv0alpha1.GitLabRepositoryConfigApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("GitRepositoryConfig"): + return &provisioningv0alpha1.GitRepositoryConfigApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("HealthStatus"): + return &provisioningv0alpha1.HealthStatusApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("LocalRepositoryConfig"): + return &provisioningv0alpha1.LocalRepositoryConfigApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("Repository"): + return &provisioningv0alpha1.RepositoryApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("RepositorySpec"): + return &provisioningv0alpha1.RepositorySpecApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("RepositoryStatus"): + return &provisioningv0alpha1.RepositoryStatusApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("ResourceCount"): + return &provisioningv0alpha1.ResourceCountApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("SyncOptions"): + return &provisioningv0alpha1.SyncOptionsApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("SyncStatus"): + return &provisioningv0alpha1.SyncStatusApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("WebhookStatus"): + return &provisioningv0alpha1.WebhookStatusApplyConfiguration{} + + } + return nil +} + +func NewTypeConverter(scheme *runtime.Scheme) *testing.TypeConverter { + return &testing.TypeConverter{Scheme: scheme, TypeResolver: internal.Parser()} +} diff --git a/apps/provisioning/pkg/generated/clientset/versioned/clientset.go b/apps/provisioning/pkg/generated/clientset/versioned/clientset.go new file mode 100644 index 00000000000..f38fb19e56e --- /dev/null +++ b/apps/provisioning/pkg/generated/clientset/versioned/clientset.go @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + fmt "fmt" + http "net/http" + + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + ProvisioningV0alpha1() provisioningv0alpha1.ProvisioningV0alpha1Interface +} + +// Clientset contains the clients for groups. +type Clientset struct { + *discovery.DiscoveryClient + provisioningV0alpha1 *provisioningv0alpha1.ProvisioningV0alpha1Client +} + +// ProvisioningV0alpha1 retrieves the ProvisioningV0alpha1Client +func (c *Clientset) ProvisioningV0alpha1() provisioningv0alpha1.ProvisioningV0alpha1Interface { + return c.provisioningV0alpha1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. +// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), +// where httpClient was generated with rest.HTTPClientFor(c). +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + + if configShallowCopy.UserAgent == "" { + configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent() + } + + // share the transport between all clients + httpClient, err := rest.HTTPClientFor(&configShallowCopy) + if err != nil { + return nil, err + } + + return NewForConfigAndClient(&configShallowCopy, httpClient) +} + +// NewForConfigAndClient creates a new Clientset for the given config and http client. +// Note the http client provided takes precedence over the configured transport values. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfigAndClient will generate a rate-limiter in configShallowCopy. +func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + + var cs Clientset + var err error + cs.provisioningV0alpha1, err = provisioningv0alpha1.NewForConfigAndClient(&configShallowCopy, httpClient) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + cs, err := NewForConfig(c) + if err != nil { + panic(err) + } + return cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.provisioningV0alpha1 = provisioningv0alpha1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/apps/provisioning/pkg/generated/clientset/versioned/fake/clientset_generated.go b/apps/provisioning/pkg/generated/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 00000000000..bcb742b634e --- /dev/null +++ b/apps/provisioning/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + applyconfiguration "github.com/grafana/grafana/apps/provisioning/pkg/generated/applyconfiguration" + clientset "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" + fakeprovisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any field management, validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +// +// DEPRECATED: NewClientset replaces this with support for field management, which significantly improves +// server side apply testing. NewClientset is only available when apply configurations are generated (e.g. +// via --with-applyconfig). +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{tracker: o} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + var opts metav1.ListOptions + if watchActcion, ok := action.(testing.WatchActionImpl); ok { + opts = watchActcion.ListOptions + } + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns, opts) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery + tracker testing.ObjectTracker +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +func (c *Clientset) Tracker() testing.ObjectTracker { + return c.tracker +} + +// NewClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewClientset(objects ...runtime.Object) *Clientset { + o := testing.NewFieldManagedObjectTracker( + scheme, + codecs.UniversalDecoder(), + applyconfiguration.NewTypeConverter(scheme), + ) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{tracker: o} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + var opts metav1.ListOptions + if watchActcion, ok := action.(testing.WatchActionImpl); ok { + opts = watchActcion.ListOptions + } + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns, opts) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +var ( + _ clientset.Interface = &Clientset{} + _ testing.FakeClient = &Clientset{} +) + +// ProvisioningV0alpha1 retrieves the ProvisioningV0alpha1Client +func (c *Clientset) ProvisioningV0alpha1() provisioningv0alpha1.ProvisioningV0alpha1Interface { + return &fakeprovisioningv0alpha1.FakeProvisioningV0alpha1{Fake: &c.Fake} +} diff --git a/apps/provisioning/pkg/generated/clientset/versioned/fake/doc.go b/apps/provisioning/pkg/generated/clientset/versioned/fake/doc.go new file mode 100644 index 00000000000..bc6b017db1b --- /dev/null +++ b/apps/provisioning/pkg/generated/clientset/versioned/fake/doc.go @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/apps/provisioning/pkg/generated/clientset/versioned/fake/register.go b/apps/provisioning/pkg/generated/clientset/versioned/fake/register.go new file mode 100644 index 00000000000..840c9e7813b --- /dev/null +++ b/apps/provisioning/pkg/generated/clientset/versioned/fake/register.go @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) + +var localSchemeBuilder = runtime.SchemeBuilder{ + provisioningv0alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(scheme)) +} diff --git a/apps/provisioning/pkg/generated/clientset/versioned/scheme/doc.go b/apps/provisioning/pkg/generated/clientset/versioned/scheme/doc.go new file mode 100644 index 00000000000..b69e1aef906 --- /dev/null +++ b/apps/provisioning/pkg/generated/clientset/versioned/scheme/doc.go @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/apps/provisioning/pkg/generated/clientset/versioned/scheme/register.go b/apps/provisioning/pkg/generated/clientset/versioned/scheme/register.go new file mode 100644 index 00000000000..133c9b3f74f --- /dev/null +++ b/apps/provisioning/pkg/generated/clientset/versioned/scheme/register.go @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + provisioningv0alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/doc.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/doc.go similarity index 100% rename from pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/doc.go rename to apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/doc.go diff --git a/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/doc.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/doc.go similarity index 100% rename from pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/doc.go rename to apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/doc.go diff --git a/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_provisioning_client.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_provisioning_client.go similarity index 83% rename from pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_provisioning_client.go rename to apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_provisioning_client.go index eb97df6de39..19a9c803579 100644 --- a/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_provisioning_client.go +++ b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_provisioning_client.go @@ -5,7 +5,7 @@ package fake import ( - v0alpha1 "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" rest "k8s.io/client-go/rest" testing "k8s.io/client-go/testing" ) diff --git a/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_repository.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_repository.go similarity index 79% rename from pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_repository.go rename to apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_repository.go index 3991d3c4ea8..15d0d1bf749 100644 --- a/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_repository.go +++ b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake/fake_repository.go @@ -5,9 +5,9 @@ package fake import ( - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - provisioningv0alpha1 "github.com/grafana/grafana/pkg/generated/applyconfiguration/provisioning/v0alpha1" - typedprovisioningv0alpha1 "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1" + typedprovisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" gentype "k8s.io/client-go/gentype" ) diff --git a/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/generated_expansion.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/generated_expansion.go similarity index 100% rename from pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/generated_expansion.go rename to apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/generated_expansion.go diff --git a/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/provisioning_client.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/provisioning_client.go similarity index 92% rename from pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/provisioning_client.go rename to apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/provisioning_client.go index 27363d30e99..75998b83300 100644 --- a/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/provisioning_client.go +++ b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/provisioning_client.go @@ -7,8 +7,8 @@ package v0alpha1 import ( http "net/http" - provisioningv0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - scheme "github.com/grafana/grafana/pkg/generated/clientset/versioned/scheme" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + scheme "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/scheme" rest "k8s.io/client-go/rest" ) diff --git a/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/repository.go b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/repository.go similarity index 91% rename from pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/repository.go rename to apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/repository.go index 9425083857b..98e7b47f40b 100644 --- a/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/repository.go +++ b/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/repository.go @@ -7,9 +7,9 @@ package v0alpha1 import ( context "context" - provisioningv0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - applyconfigurationprovisioningv0alpha1 "github.com/grafana/grafana/pkg/generated/applyconfiguration/provisioning/v0alpha1" - scheme "github.com/grafana/grafana/pkg/generated/clientset/versioned/scheme" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + applyconfigurationprovisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/applyconfiguration/provisioning/v0alpha1" + scheme "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/scheme" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" diff --git a/apps/provisioning/pkg/generated/informers/externalversions/factory.go b/apps/provisioning/pkg/generated/informers/externalversions/factory.go new file mode 100644 index 00000000000..fb8443331f2 --- /dev/null +++ b/apps/provisioning/pkg/generated/informers/externalversions/factory.go @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + versioned "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned" + internalinterfaces "github.com/grafana/grafana/apps/provisioning/pkg/generated/informers/externalversions/internalinterfaces" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/generated/informers/externalversions/provisioning" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + transform cache.TransformFunc + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool + // wg tracks how many goroutines were started. + wg sync.WaitGroup + // shuttingDown is true when Shutdown has been called. It may still be running + // because it needs to wait for goroutines. + shuttingDown bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// WithTransform sets a transform on all informers. +func WithTransform(transform cache.TransformFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.transform = transform + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + if f.shuttingDown { + return + } + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + f.wg.Add(1) + // We need a new variable in each loop iteration, + // otherwise the goroutine would use the loop variable + // and that keeps changing. + informer := informer + go func() { + defer f.wg.Done() + informer.Run(stopCh) + }() + f.startedInformers[informerType] = true + } + } +} + +func (f *sharedInformerFactory) Shutdown() { + f.lock.Lock() + f.shuttingDown = true + f.lock.Unlock() + + // Will return immediately if there is nothing to wait for. + f.wg.Wait() +} + +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + informer.SetTransform(f.transform) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +// +// It is typically used like this: +// +// ctx, cancel := context.Background() +// defer cancel() +// factory := NewSharedInformerFactory(client, resyncPeriod) +// defer factory.WaitForStop() // Returns immediately if nothing was started. +// genericInformer := factory.ForResource(resource) +// typedInformer := factory.SomeAPIGroup().V1().SomeType() +// factory.Start(ctx.Done()) // Start processing these informers. +// synced := factory.WaitForCacheSync(ctx.Done()) +// for v, ok := range synced { +// if !ok { +// fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v) +// return +// } +// } +// +// // Creating informers can also be created after Start, but then +// // Start must be called again: +// anotherGenericInformer := factory.ForResource(resource) +// factory.Start(ctx.Done()) +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + + // Start initializes all requested informers. They are handled in goroutines + // which run until the stop channel gets closed. + // Warning: Start does not block. When run in a go-routine, it will race with a later WaitForCacheSync. + Start(stopCh <-chan struct{}) + + // Shutdown marks a factory as shutting down. At that point no new + // informers can be started anymore and Start will return without + // doing anything. + // + // In addition, Shutdown blocks until all goroutines have terminated. For that + // to happen, the close channel(s) that they were started with must be closed, + // either before Shutdown gets called or while it is waiting. + // + // Shutdown may be called multiple times, even concurrently. All such calls will + // block until all goroutines have terminated. + Shutdown() + + // WaitForCacheSync blocks until all started informers' caches were synced + // or the stop channel gets closed. + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + // ForResource gives generic access to a shared informer of the matching type. + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + + // InformerFor returns the SharedIndexInformer for obj using an internal + // client. + InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer + + Provisioning() provisioning.Interface +} + +func (f *sharedInformerFactory) Provisioning() provisioning.Interface { + return provisioning.New(f, f.namespace, f.tweakListOptions) +} diff --git a/apps/provisioning/pkg/generated/informers/externalversions/generic.go b/apps/provisioning/pkg/generated/informers/externalversions/generic.go new file mode 100644 index 00000000000..d4b7d049e54 --- /dev/null +++ b/apps/provisioning/pkg/generated/informers/externalversions/generic.go @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + fmt "fmt" + + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=provisioning.grafana.app, Version=v0alpha1 + case v0alpha1.SchemeGroupVersion.WithResource("repositories"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Provisioning().V0alpha1().Repositories().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/apps/provisioning/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/apps/provisioning/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 00000000000..06fe98fc993 --- /dev/null +++ b/apps/provisioning/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: AGPL-3.0-only + +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + versioned "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" +) + +// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +// TweakListOptionsFunc is a function that transforms a v1.ListOptions. +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/generated/informers/externalversions/provisioning/interface.go b/apps/provisioning/pkg/generated/informers/externalversions/provisioning/interface.go similarity index 78% rename from pkg/generated/informers/externalversions/provisioning/interface.go rename to apps/provisioning/pkg/generated/informers/externalversions/provisioning/interface.go index 851568050e0..7961fd26712 100644 --- a/pkg/generated/informers/externalversions/provisioning/interface.go +++ b/apps/provisioning/pkg/generated/informers/externalversions/provisioning/interface.go @@ -5,8 +5,8 @@ package provisioning import ( - internalinterfaces "github.com/grafana/grafana/pkg/generated/informers/externalversions/internalinterfaces" - v0alpha1 "github.com/grafana/grafana/pkg/generated/informers/externalversions/provisioning/v0alpha1" + internalinterfaces "github.com/grafana/grafana/apps/provisioning/pkg/generated/informers/externalversions/internalinterfaces" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1" ) // Interface provides access to each of this group's versions. diff --git a/pkg/generated/informers/externalversions/provisioning/v0alpha1/interface.go b/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/interface.go similarity index 88% rename from pkg/generated/informers/externalversions/provisioning/v0alpha1/interface.go rename to apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/interface.go index 51060296a02..b358790c61e 100644 --- a/pkg/generated/informers/externalversions/provisioning/v0alpha1/interface.go +++ b/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/interface.go @@ -5,7 +5,7 @@ package v0alpha1 import ( - internalinterfaces "github.com/grafana/grafana/pkg/generated/informers/externalversions/internalinterfaces" + internalinterfaces "github.com/grafana/grafana/apps/provisioning/pkg/generated/informers/externalversions/internalinterfaces" ) // Interface provides access to all the informers in this group version. diff --git a/pkg/generated/informers/externalversions/provisioning/v0alpha1/repository.go b/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/repository.go similarity index 88% rename from pkg/generated/informers/externalversions/provisioning/v0alpha1/repository.go rename to apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/repository.go index 58df4bad73d..025ce07d20f 100644 --- a/pkg/generated/informers/externalversions/provisioning/v0alpha1/repository.go +++ b/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1/repository.go @@ -8,10 +8,10 @@ import ( context "context" time "time" - apisprovisioningv0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - versioned "github.com/grafana/grafana/pkg/generated/clientset/versioned" - internalinterfaces "github.com/grafana/grafana/pkg/generated/informers/externalversions/internalinterfaces" - provisioningv0alpha1 "github.com/grafana/grafana/pkg/generated/listers/provisioning/v0alpha1" + apisprovisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + versioned "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned" + internalinterfaces "github.com/grafana/grafana/apps/provisioning/pkg/generated/informers/externalversions/internalinterfaces" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" diff --git a/pkg/generated/listers/provisioning/v0alpha1/expansion_generated.go b/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/expansion_generated.go similarity index 100% rename from pkg/generated/listers/provisioning/v0alpha1/expansion_generated.go rename to apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/expansion_generated.go diff --git a/pkg/generated/listers/provisioning/v0alpha1/repository.go b/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/repository.go similarity index 95% rename from pkg/generated/listers/provisioning/v0alpha1/repository.go rename to apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/repository.go index 969cb034e88..4196743bc1b 100644 --- a/pkg/generated/listers/provisioning/v0alpha1/repository.go +++ b/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1/repository.go @@ -5,7 +5,7 @@ package v0alpha1 import ( - provisioningv0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioningv0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" labels "k8s.io/apimachinery/pkg/labels" listers "k8s.io/client-go/listers" cache "k8s.io/client-go/tools/cache" diff --git a/apps/secret/inline/v1beta1/inline.pb.go b/apps/secret/inline/v1beta1/inline.pb.go new file mode 100644 index 00000000000..d759693f4e0 --- /dev/null +++ b/apps/secret/inline/v1beta1/inline.pb.go @@ -0,0 +1,515 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.5 +// protoc (unknown) +// source: inline/v1beta1/inline.proto + +package inlinev1beta1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ObjectReference struct { + state protoimpl.MessageState `protogen:"open.v1"` + // APIGroup is the name of the API group that contains the referred object. + // The empty string represents the core API group. + ApiGroup string `protobuf:"bytes,1,opt,name=api_group,json=apiGroup,proto3" json:"api_group,omitempty"` + // APIVersion is the version of the API group that contains the referred object. + ApiVersion string `protobuf:"bytes,2,opt,name=api_version,json=apiVersion,proto3" json:"api_version,omitempty"` + // See https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds + Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` + // Tenant isolation + Namespace string `protobuf:"bytes,4,opt,name=namespace,proto3" json:"namespace,omitempty"` + // Explicit resource identifier + Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ObjectReference) Reset() { + *x = ObjectReference{} + mi := &file_inline_v1beta1_inline_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ObjectReference) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ObjectReference) ProtoMessage() {} + +func (x *ObjectReference) ProtoReflect() protoreflect.Message { + mi := &file_inline_v1beta1_inline_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ObjectReference.ProtoReflect.Descriptor instead. +func (*ObjectReference) Descriptor() ([]byte, []int) { + return file_inline_v1beta1_inline_proto_rawDescGZIP(), []int{0} +} + +func (x *ObjectReference) GetApiGroup() string { + if x != nil { + return x.ApiGroup + } + return "" +} + +func (x *ObjectReference) GetApiVersion() string { + if x != nil { + return x.ApiVersion + } + return "" +} + +func (x *ObjectReference) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +func (x *ObjectReference) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *ObjectReference) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type CanReferenceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Owner reference. + Owner *ObjectReference `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` + // Secure value names to check. + Names []string `protobuf:"bytes,2,rep,name=names,proto3" json:"names,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CanReferenceRequest) Reset() { + *x = CanReferenceRequest{} + mi := &file_inline_v1beta1_inline_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CanReferenceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CanReferenceRequest) ProtoMessage() {} + +func (x *CanReferenceRequest) ProtoReflect() protoreflect.Message { + mi := &file_inline_v1beta1_inline_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CanReferenceRequest.ProtoReflect.Descriptor instead. +func (*CanReferenceRequest) Descriptor() ([]byte, []int) { + return file_inline_v1beta1_inline_proto_rawDescGZIP(), []int{1} +} + +func (x *CanReferenceRequest) GetOwner() *ObjectReference { + if x != nil { + return x.Owner + } + return nil +} + +func (x *CanReferenceRequest) GetNames() []string { + if x != nil { + return x.Names + } + return nil +} + +type CanReferenceResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CanReferenceResponse) Reset() { + *x = CanReferenceResponse{} + mi := &file_inline_v1beta1_inline_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CanReferenceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CanReferenceResponse) ProtoMessage() {} + +func (x *CanReferenceResponse) ProtoReflect() protoreflect.Message { + mi := &file_inline_v1beta1_inline_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CanReferenceResponse.ProtoReflect.Descriptor instead. +func (*CanReferenceResponse) Descriptor() ([]byte, []int) { + return file_inline_v1beta1_inline_proto_rawDescGZIP(), []int{2} +} + +type CreateInlineRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Owner reference. + Owner *ObjectReference `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` + // Raw secret value. + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateInlineRequest) Reset() { + *x = CreateInlineRequest{} + mi := &file_inline_v1beta1_inline_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateInlineRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateInlineRequest) ProtoMessage() {} + +func (x *CreateInlineRequest) ProtoReflect() protoreflect.Message { + mi := &file_inline_v1beta1_inline_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateInlineRequest.ProtoReflect.Descriptor instead. +func (*CreateInlineRequest) Descriptor() ([]byte, []int) { + return file_inline_v1beta1_inline_proto_rawDescGZIP(), []int{3} +} + +func (x *CreateInlineRequest) GetOwner() *ObjectReference { + if x != nil { + return x.Owner + } + return nil +} + +func (x *CreateInlineRequest) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type CreateInlineResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The name of the created secure value. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateInlineResponse) Reset() { + *x = CreateInlineResponse{} + mi := &file_inline_v1beta1_inline_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateInlineResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateInlineResponse) ProtoMessage() {} + +func (x *CreateInlineResponse) ProtoReflect() protoreflect.Message { + mi := &file_inline_v1beta1_inline_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateInlineResponse.ProtoReflect.Descriptor instead. +func (*CreateInlineResponse) Descriptor() ([]byte, []int) { + return file_inline_v1beta1_inline_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateInlineResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type DeleteWhenOwnedByResourceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Owner reference. + Owner *ObjectReference `protobuf:"bytes,1,opt,name=owner,proto3" json:"owner,omitempty"` + // Name of the secure value to delete. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteWhenOwnedByResourceRequest) Reset() { + *x = DeleteWhenOwnedByResourceRequest{} + mi := &file_inline_v1beta1_inline_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteWhenOwnedByResourceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteWhenOwnedByResourceRequest) ProtoMessage() {} + +func (x *DeleteWhenOwnedByResourceRequest) ProtoReflect() protoreflect.Message { + mi := &file_inline_v1beta1_inline_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteWhenOwnedByResourceRequest.ProtoReflect.Descriptor instead. +func (*DeleteWhenOwnedByResourceRequest) Descriptor() ([]byte, []int) { + return file_inline_v1beta1_inline_proto_rawDescGZIP(), []int{5} +} + +func (x *DeleteWhenOwnedByResourceRequest) GetOwner() *ObjectReference { + if x != nil { + return x.Owner + } + return nil +} + +func (x *DeleteWhenOwnedByResourceRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type DeleteWhenOwnedByResourceResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteWhenOwnedByResourceResponse) Reset() { + *x = DeleteWhenOwnedByResourceResponse{} + mi := &file_inline_v1beta1_inline_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteWhenOwnedByResourceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteWhenOwnedByResourceResponse) ProtoMessage() {} + +func (x *DeleteWhenOwnedByResourceResponse) ProtoReflect() protoreflect.Message { + mi := &file_inline_v1beta1_inline_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteWhenOwnedByResourceResponse.ProtoReflect.Descriptor instead. +func (*DeleteWhenOwnedByResourceResponse) Descriptor() ([]byte, []int) { + return file_inline_v1beta1_inline_proto_rawDescGZIP(), []int{6} +} + +var File_inline_v1beta1_inline_proto protoreflect.FileDescriptor + +var file_inline_v1beta1_inline_proto_rawDesc = string([]byte{ + 0x0a, 0x1b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x69, + 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x22, 0x95, 0x01, 0x0a, + 0x0f, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x70, 0x69, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1f, 0x0a, + 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x61, 0x0a, 0x13, 0x43, 0x61, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x05, 0x6f, + 0x77, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, + 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x43, 0x61, 0x6e, 0x52, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x61, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x22, 0x2a, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x6c, + 0x0a, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x68, 0x65, 0x6e, 0x4f, 0x77, 0x6e, 0x65, + 0x64, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x34, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x23, 0x0a, 0x21, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x68, 0x65, 0x6e, 0x4f, 0x77, 0x6e, 0x65, 0x64, 0x42, + 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x32, 0xcc, 0x02, 0x0a, 0x18, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x65, 0x63, 0x75, + 0x72, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x57, + 0x0a, 0x0c, 0x43, 0x61, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x22, + 0x2e, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, + 0x61, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x22, 0x2e, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x69, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x7e, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x68, 0x65, 0x6e, 0x4f, 0x77, + 0x6e, 0x65, 0x64, 0x42, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2f, 0x2e, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x57, 0x68, 0x65, 0x6e, 0x4f, 0x77, 0x6e, 0x65, 0x64, 0x42, 0x79, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, + 0x2e, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x68, 0x65, 0x6e, 0x4f, 0x77, 0x6e, 0x65, 0x64, 0x42, 0x79, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x42, 0x45, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x61, + 0x70, 0x70, 0x73, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x2f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +}) + +var ( + file_inline_v1beta1_inline_proto_rawDescOnce sync.Once + file_inline_v1beta1_inline_proto_rawDescData []byte +) + +func file_inline_v1beta1_inline_proto_rawDescGZIP() []byte { + file_inline_v1beta1_inline_proto_rawDescOnce.Do(func() { + file_inline_v1beta1_inline_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_inline_v1beta1_inline_proto_rawDesc), len(file_inline_v1beta1_inline_proto_rawDesc))) + }) + return file_inline_v1beta1_inline_proto_rawDescData +} + +var file_inline_v1beta1_inline_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_inline_v1beta1_inline_proto_goTypes = []any{ + (*ObjectReference)(nil), // 0: inlinev1beta1.ObjectReference + (*CanReferenceRequest)(nil), // 1: inlinev1beta1.CanReferenceRequest + (*CanReferenceResponse)(nil), // 2: inlinev1beta1.CanReferenceResponse + (*CreateInlineRequest)(nil), // 3: inlinev1beta1.CreateInlineRequest + (*CreateInlineResponse)(nil), // 4: inlinev1beta1.CreateInlineResponse + (*DeleteWhenOwnedByResourceRequest)(nil), // 5: inlinev1beta1.DeleteWhenOwnedByResourceRequest + (*DeleteWhenOwnedByResourceResponse)(nil), // 6: inlinev1beta1.DeleteWhenOwnedByResourceResponse +} +var file_inline_v1beta1_inline_proto_depIdxs = []int32{ + 0, // 0: inlinev1beta1.CanReferenceRequest.owner:type_name -> inlinev1beta1.ObjectReference + 0, // 1: inlinev1beta1.CreateInlineRequest.owner:type_name -> inlinev1beta1.ObjectReference + 0, // 2: inlinev1beta1.DeleteWhenOwnedByResourceRequest.owner:type_name -> inlinev1beta1.ObjectReference + 1, // 3: inlinev1beta1.InlineSecureValueService.CanReference:input_type -> inlinev1beta1.CanReferenceRequest + 3, // 4: inlinev1beta1.InlineSecureValueService.CreateInline:input_type -> inlinev1beta1.CreateInlineRequest + 5, // 5: inlinev1beta1.InlineSecureValueService.DeleteWhenOwnedByResource:input_type -> inlinev1beta1.DeleteWhenOwnedByResourceRequest + 2, // 6: inlinev1beta1.InlineSecureValueService.CanReference:output_type -> inlinev1beta1.CanReferenceResponse + 4, // 7: inlinev1beta1.InlineSecureValueService.CreateInline:output_type -> inlinev1beta1.CreateInlineResponse + 6, // 8: inlinev1beta1.InlineSecureValueService.DeleteWhenOwnedByResource:output_type -> inlinev1beta1.DeleteWhenOwnedByResourceResponse + 6, // [6:9] is the sub-list for method output_type + 3, // [3:6] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_inline_v1beta1_inline_proto_init() } +func file_inline_v1beta1_inline_proto_init() { + if File_inline_v1beta1_inline_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_inline_v1beta1_inline_proto_rawDesc), len(file_inline_v1beta1_inline_proto_rawDesc)), + NumEnums: 0, + NumMessages: 7, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_inline_v1beta1_inline_proto_goTypes, + DependencyIndexes: file_inline_v1beta1_inline_proto_depIdxs, + MessageInfos: file_inline_v1beta1_inline_proto_msgTypes, + }.Build() + File_inline_v1beta1_inline_proto = out.File + file_inline_v1beta1_inline_proto_goTypes = nil + file_inline_v1beta1_inline_proto_depIdxs = nil +} diff --git a/apps/secret/inline/v1beta1/inline.proto b/apps/secret/inline/v1beta1/inline.proto new file mode 100644 index 00000000000..493b855c517 --- /dev/null +++ b/apps/secret/inline/v1beta1/inline.proto @@ -0,0 +1,67 @@ +syntax = "proto3"; + +package inlinev1beta1; + +option go_package = "github.com/grafana/grafana/apps/secret/inline/v1beta1;inlinev1beta1"; + +message ObjectReference { + // APIGroup is the name of the API group that contains the referred object. + // The empty string represents the core API group. + string api_group = 1; + + // APIVersion is the version of the API group that contains the referred object. + string api_version = 2; + + // See https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds + string kind = 3; + + // Tenant isolation + string namespace = 4; + + // Explicit resource identifier + string name = 5; +} + +message CanReferenceRequest { + // Owner reference. + ObjectReference owner = 1; + + // Secure value names to check. + repeated string names = 2; +} + +message CanReferenceResponse {} + +message CreateInlineRequest { + // Owner reference. + ObjectReference owner = 1; + + // Raw secret value. + string value = 2; +} + +message CreateInlineResponse { + // The name of the created secure value. + string name = 1; +} + +message DeleteWhenOwnedByResourceRequest { + // Owner reference. + ObjectReference owner = 1; + + // Name of the secure value to delete. + string name = 2; +} + +message DeleteWhenOwnedByResourceResponse {} + +service InlineSecureValueService { + // Check that the request user can reference a secret in the context of a given resource (owner) + rpc CanReference(CanReferenceRequest) returns (CanReferenceResponse); + + // CreateInline creates a secret that is owned by the referenced object. Returns the name of the created secret or an error + rpc CreateInline(CreateInlineRequest) returns (CreateInlineResponse); + + // DeleteWhenOwnedByResource removes secrets if and only if they are owned by a referenced object + rpc DeleteWhenOwnedByResource(DeleteWhenOwnedByResourceRequest) returns (DeleteWhenOwnedByResourceResponse); +} diff --git a/apps/secret/inline/v1beta1/inline_grpc.pb.go b/apps/secret/inline/v1beta1/inline_grpc.pb.go new file mode 100644 index 00000000000..2ce117adb7d --- /dev/null +++ b/apps/secret/inline/v1beta1/inline_grpc.pb.go @@ -0,0 +1,190 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.4.0 +// - protoc (unknown) +// source: inline/v1beta1/inline.proto + +package inlinev1beta1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 + +const ( + InlineSecureValueService_CanReference_FullMethodName = "/inlinev1beta1.InlineSecureValueService/CanReference" + InlineSecureValueService_CreateInline_FullMethodName = "/inlinev1beta1.InlineSecureValueService/CreateInline" + InlineSecureValueService_DeleteWhenOwnedByResource_FullMethodName = "/inlinev1beta1.InlineSecureValueService/DeleteWhenOwnedByResource" +) + +// InlineSecureValueServiceClient is the client API for InlineSecureValueService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type InlineSecureValueServiceClient interface { + // Check that the request user can reference a secret in the context of a given resource (owner) + CanReference(ctx context.Context, in *CanReferenceRequest, opts ...grpc.CallOption) (*CanReferenceResponse, error) + // CreateInline creates a secret that is owned by the referenced object. Returns the name of the created secret or an error + CreateInline(ctx context.Context, in *CreateInlineRequest, opts ...grpc.CallOption) (*CreateInlineResponse, error) + // DeleteWhenOwnedByResource removes secrets if and only if they are owned by a referenced object + DeleteWhenOwnedByResource(ctx context.Context, in *DeleteWhenOwnedByResourceRequest, opts ...grpc.CallOption) (*DeleteWhenOwnedByResourceResponse, error) +} + +type inlineSecureValueServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewInlineSecureValueServiceClient(cc grpc.ClientConnInterface) InlineSecureValueServiceClient { + return &inlineSecureValueServiceClient{cc} +} + +func (c *inlineSecureValueServiceClient) CanReference(ctx context.Context, in *CanReferenceRequest, opts ...grpc.CallOption) (*CanReferenceResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CanReferenceResponse) + err := c.cc.Invoke(ctx, InlineSecureValueService_CanReference_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *inlineSecureValueServiceClient) CreateInline(ctx context.Context, in *CreateInlineRequest, opts ...grpc.CallOption) (*CreateInlineResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateInlineResponse) + err := c.cc.Invoke(ctx, InlineSecureValueService_CreateInline_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *inlineSecureValueServiceClient) DeleteWhenOwnedByResource(ctx context.Context, in *DeleteWhenOwnedByResourceRequest, opts ...grpc.CallOption) (*DeleteWhenOwnedByResourceResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DeleteWhenOwnedByResourceResponse) + err := c.cc.Invoke(ctx, InlineSecureValueService_DeleteWhenOwnedByResource_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// InlineSecureValueServiceServer is the server API for InlineSecureValueService service. +// All implementations should embed UnimplementedInlineSecureValueServiceServer +// for forward compatibility +type InlineSecureValueServiceServer interface { + // Check that the request user can reference a secret in the context of a given resource (owner) + CanReference(context.Context, *CanReferenceRequest) (*CanReferenceResponse, error) + // CreateInline creates a secret that is owned by the referenced object. Returns the name of the created secret or an error + CreateInline(context.Context, *CreateInlineRequest) (*CreateInlineResponse, error) + // DeleteWhenOwnedByResource removes secrets if and only if they are owned by a referenced object + DeleteWhenOwnedByResource(context.Context, *DeleteWhenOwnedByResourceRequest) (*DeleteWhenOwnedByResourceResponse, error) +} + +// UnimplementedInlineSecureValueServiceServer should be embedded to have forward compatible implementations. +type UnimplementedInlineSecureValueServiceServer struct { +} + +func (UnimplementedInlineSecureValueServiceServer) CanReference(context.Context, *CanReferenceRequest) (*CanReferenceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CanReference not implemented") +} +func (UnimplementedInlineSecureValueServiceServer) CreateInline(context.Context, *CreateInlineRequest) (*CreateInlineResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateInline not implemented") +} +func (UnimplementedInlineSecureValueServiceServer) DeleteWhenOwnedByResource(context.Context, *DeleteWhenOwnedByResourceRequest) (*DeleteWhenOwnedByResourceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteWhenOwnedByResource not implemented") +} + +// UnsafeInlineSecureValueServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to InlineSecureValueServiceServer will +// result in compilation errors. +type UnsafeInlineSecureValueServiceServer interface { + mustEmbedUnimplementedInlineSecureValueServiceServer() +} + +func RegisterInlineSecureValueServiceServer(s grpc.ServiceRegistrar, srv InlineSecureValueServiceServer) { + s.RegisterService(&InlineSecureValueService_ServiceDesc, srv) +} + +func _InlineSecureValueService_CanReference_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CanReferenceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InlineSecureValueServiceServer).CanReference(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: InlineSecureValueService_CanReference_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InlineSecureValueServiceServer).CanReference(ctx, req.(*CanReferenceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _InlineSecureValueService_CreateInline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateInlineRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InlineSecureValueServiceServer).CreateInline(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: InlineSecureValueService_CreateInline_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InlineSecureValueServiceServer).CreateInline(ctx, req.(*CreateInlineRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _InlineSecureValueService_DeleteWhenOwnedByResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteWhenOwnedByResourceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(InlineSecureValueServiceServer).DeleteWhenOwnedByResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: InlineSecureValueService_DeleteWhenOwnedByResource_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(InlineSecureValueServiceServer).DeleteWhenOwnedByResource(ctx, req.(*DeleteWhenOwnedByResourceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// InlineSecureValueService_ServiceDesc is the grpc.ServiceDesc for InlineSecureValueService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var InlineSecureValueService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "inlinev1beta1.InlineSecureValueService", + HandlerType: (*InlineSecureValueServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CanReference", + Handler: _InlineSecureValueService_CanReference_Handler, + }, + { + MethodName: "CreateInline", + Handler: _InlineSecureValueService_CreateInline_Handler, + }, + { + MethodName: "DeleteWhenOwnedByResource", + Handler: _InlineSecureValueService_DeleteWhenOwnedByResource_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "inline/v1beta1/inline.proto", +} diff --git a/apps/shorturl/Makefile b/apps/shorturl/Makefile new file mode 100644 index 00000000000..230bfd4149a --- /dev/null +++ b/apps/shorturl/Makefile @@ -0,0 +1,9 @@ +include ../sdk.mk + +.PHONY: generate # Run Grafana App SDK code generation +generate: install-app-sdk update-app-sdk + @$(APP_SDK_BIN) generate \ + --source=./kinds/ \ + --gogenpath=./pkg/apis \ + --grouping=group \ + --defencoding=none \ No newline at end of file diff --git a/apps/shorturl/go.mod b/apps/shorturl/go.mod new file mode 100644 index 00000000000..deaba460063 --- /dev/null +++ b/apps/shorturl/go.mod @@ -0,0 +1,92 @@ +module github.com/grafana/grafana/apps/shorturl + +go 1.24.0 + +toolchain go1.24.5 + +require ( + github.com/grafana/grafana-app-sdk v0.40.2 + k8s.io/apimachinery v0.33.3 + k8s.io/klog/v2 v2.130.1 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect + github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.12.1 // indirect + github.com/evanphx/json-patch v5.9.11+incompatible // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/getkin/kin-openapi v0.132.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-test/deep v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grafana/grafana-app-sdk/logging v0.40.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect + github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect + github.com/onsi/ginkgo/v2 v2.22.2 // indirect + github.com/onsi/gomega v1.36.2 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/spf13/pflag v1.0.7 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.37.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect + go.opentelemetry.io/otel/metric v1.37.0 // indirect + go.opentelemetry.io/otel/sdk v1.37.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect + go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/term v0.33.0 // indirect + golang.org/x/text v0.27.0 // indirect + golang.org/x/time v0.11.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect + google.golang.org/grpc v1.74.2 // indirect + google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.33.3 // indirect + k8s.io/apiextensions-apiserver v0.33.3 // indirect + k8s.io/client-go v0.33.3 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect + sigs.k8s.io/yaml v1.5.0 // indirect +) diff --git a/apps/shorturl/go.sum b/apps/shorturl/go.sum new file mode 100644 index 00000000000..88512ac2ed8 --- /dev/null +++ b/apps/shorturl/go.sum @@ -0,0 +1,236 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= +github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= +github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grafana/grafana-app-sdk v0.40.2 h1:j2ftFuqhX+exYUipfEjeWDs3i7oiJkweTF8gFLL7wWU= +github.com/grafana/grafana-app-sdk v0.40.2/go.mod h1:BbNXPNki3mtbkWxYqJsyA1Cj9AShSyaY33z8WkyfVv0= +github.com/grafana/grafana-app-sdk/logging v0.40.1 h1:ru+GqbaQk6jthA5l2Yo1WI/JbNXKNQmLiqNrxz7HGP4= +github.com/grafana/grafana-app-sdk/logging v0.40.1/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= +github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= +github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= +github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA= +go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= +go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= +go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= +golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= +gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY= +google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= +google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= +k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= +k8s.io/apiextensions-apiserver v0.33.3 h1:qmOcAHN6DjfD0v9kxL5udB27SRP6SG/MTopmge3MwEs= +k8s.io/apiextensions-apiserver v0.33.3/go.mod h1:oROuctgo27mUsyp9+Obahos6CWcMISSAPzQ77CAQGz8= +k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= +k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= +k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ= +sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4= diff --git a/apps/shorturl/kinds/cue.mod/module.cue b/apps/shorturl/kinds/cue.mod/module.cue new file mode 100644 index 00000000000..2e6cdd1ee5b --- /dev/null +++ b/apps/shorturl/kinds/cue.mod/module.cue @@ -0,0 +1,2 @@ +module: "github.com/grafana/grafana/apps/shorturl/kinds" +language: version: "v0.8.2" diff --git a/apps/shorturl/kinds/manifest.cue b/apps/shorturl/kinds/manifest.cue new file mode 100644 index 00000000000..27519d040af --- /dev/null +++ b/apps/shorturl/kinds/manifest.cue @@ -0,0 +1,68 @@ +package kinds + +manifest: { + // appName is the unique name of your app. It is used to reference the app from other config objects, + // and to generate the group used by your app in the app platform API. + appName: "shorturl" + groupOverride: "shorturl.grafana.app" + // groupOverride can be used to specify a non-appName-based API group. + // By default, an app's API group is LOWER(REPLACE(appName, '-', '')).ext.grafana.com, + // but there are cases where this needs to be changed. + // Keep in mind that changing this after an app is deployed can cause problems with clients and/or kind data. + // groupOverride: foo.ext.grafana.app + + // versions is a map of versions supported by your app. Version names should follow the format "v" or + // "v(alpha|beta)". Each version contains the kinds your app manages for that version. + // If your app needs access to kinds managed by another app, use permissions.accessKinds to allow your app access. + versions: { + "v1alpha1": v1alpha1 + } + // extraPermissions contains any additional permissions your app may require to function. + // Your app will always have all permissions for each kind it manages (the items defined in 'kinds'). + extraPermissions: { + // If your app needs access to additional kinds supplied by other apps, you can list them here + accessKinds: [ + // Here is an example for your app accessing the playlist kind for reads and watch + // { + // group: "playlist.grafana.app" + // resource: "playlists" + // actions: ["get","list","watch"] + // } + ] + } +} + +// v1alpha1 is the v1alpha1 version of the app's API. +// It includes kinds which the v1alpha1 API serves, and (future) custom routes served globally from the v1alpha1 version. +v1alpha1: { + // kinds is the list of kinds served by this version + kinds: [shorturl] + // [OPTIONAL] + // served indicates whether this particular version is served by the API server. + // served should be set to false before a version is removed from the manifest entirely. + // served defaults to true if not present. + served: true + // [OPTIONAL] + // Codegen is a trait that tells the grafana-app-sdk, or other code generation tooling, how to process this kind. + // If not present, default values within the codegen trait are used. + // If you wish to specify codegen per-version, put this section in the version's object + // (for example, v1alpha1) instead. + codegen: { + // [OPTIONAL] + // ts contains TypeScript code generation properties for the kind + ts: { + // [OPTIONAL] + // enabled indicates whether the CLI should generate front-end TypeScript code for the kind. + // Defaults to true if not present. + enabled: true + } + // [OPTIONAL] + // go contains go code generation properties for the kind + go: { + // [OPTIONAL] + // enabled indicates whether the CLI should generate back-end go code for the kind. + // Defaults to true if not present. + enabled: true + } + } +} diff --git a/apps/shorturl/kinds/shorturl.cue b/apps/shorturl/kinds/shorturl.cue new file mode 100644 index 00000000000..d88e10ad3e3 --- /dev/null +++ b/apps/shorturl/kinds/shorturl.cue @@ -0,0 +1,16 @@ +package kinds + +shorturl: { + kind: "ShortURL" + pluralName: "ShortURLs" + schema: { + spec: { + // The original path to where the short url is linking too e.g. https://localhost:3000/eer8i1kictngga/new-dashboard-with-lib-panel + path: string + } + status: { + // The last time the short URL was used, 0 is the initial value + lastSeenAt: int64 + } + } +} diff --git a/apps/shorturl/pkg/apis/shorturl/v1alpha1/constants.go b/apps/shorturl/pkg/apis/shorturl/v1alpha1/constants.go new file mode 100644 index 00000000000..e6000ff1f42 --- /dev/null +++ b/apps/shorturl/pkg/apis/shorturl/v1alpha1/constants.go @@ -0,0 +1,18 @@ +package v1alpha1 + +import "k8s.io/apimachinery/pkg/runtime/schema" + +const ( + // APIGroup is the API group used by all kinds in this package + APIGroup = "shorturl.grafana.app" + // APIVersion is the API version used by all kinds in this package + APIVersion = "v1alpha1" +) + +var ( + // GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package + GroupVersion = schema.GroupVersion{ + Group: APIGroup, + Version: APIVersion, + } +) diff --git a/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_codec_gen.go b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_codec_gen.go new file mode 100644 index 00000000000..15d7493bb80 --- /dev/null +++ b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_codec_gen.go @@ -0,0 +1,28 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v1alpha1 + +import ( + "encoding/json" + "io" + + "github.com/grafana/grafana-app-sdk/resource" +) + +// ShortURLJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding +type ShortURLJSONCodec struct{} + +// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into` +func (*ShortURLJSONCodec) Read(reader io.Reader, into resource.Object) error { + return json.NewDecoder(reader).Decode(into) +} + +// Write writes JSON-encoded bytes into `writer` marshaled from `from` +func (*ShortURLJSONCodec) Write(writer io.Writer, from resource.Object) error { + return json.NewEncoder(writer).Encode(from) +} + +// Interface compliance checks +var _ resource.Codec = &ShortURLJSONCodec{} diff --git a/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_metadata_gen.go b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_metadata_gen.go new file mode 100644 index 00000000000..e7509c1a38e --- /dev/null +++ b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_metadata_gen.go @@ -0,0 +1,31 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" +) + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +type ShortURLMetadata struct { + UpdateTimestamp time.Time `json:"updateTimestamp"` + CreatedBy string `json:"createdBy"` + Uid string `json:"uid"` + CreationTimestamp time.Time `json:"creationTimestamp"` + DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"` + Finalizers []string `json:"finalizers"` + ResourceVersion string `json:"resourceVersion"` + Generation int64 `json:"generation"` + UpdatedBy string `json:"updatedBy"` + Labels map[string]string `json:"labels"` +} + +// NewShortURLMetadata creates a new ShortURLMetadata object. +func NewShortURLMetadata() *ShortURLMetadata { + return &ShortURLMetadata{ + Finalizers: []string{}, + Labels: map[string]string{}, + } +} diff --git a/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_object_gen.go b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_object_gen.go new file mode 100644 index 00000000000..734af74aae3 --- /dev/null +++ b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_object_gen.go @@ -0,0 +1,319 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v1alpha1 + +import ( + "fmt" + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "time" +) + +// +k8s:openapi-gen=true +type ShortURL struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ObjectMeta `json:"metadata" yaml:"metadata"` + + // Spec is the spec of the ShortURL + Spec ShortURLSpec `json:"spec" yaml:"spec"` + + Status ShortURLStatus `json:"status" yaml:"status"` +} + +func (o *ShortURL) GetSpec() any { + return o.Spec +} + +func (o *ShortURL) SetSpec(spec any) error { + cast, ok := spec.(ShortURLSpec) + if !ok { + return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec) + } + o.Spec = cast + return nil +} + +func (o *ShortURL) GetSubresources() map[string]any { + return map[string]any{ + "status": o.Status, + } +} + +func (o *ShortURL) GetSubresource(name string) (any, bool) { + switch name { + case "status": + return o.Status, true + default: + return nil, false + } +} + +func (o *ShortURL) SetSubresource(name string, value any) error { + switch name { + case "status": + cast, ok := value.(ShortURLStatus) + if !ok { + return fmt.Errorf("cannot set status type %#v, not of type ShortURLStatus", value) + } + o.Status = cast + return nil + default: + return fmt.Errorf("subresource '%s' does not exist", name) + } +} + +func (o *ShortURL) GetStaticMetadata() resource.StaticMetadata { + gvk := o.GroupVersionKind() + return resource.StaticMetadata{ + Name: o.ObjectMeta.Name, + Namespace: o.ObjectMeta.Namespace, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + } +} + +func (o *ShortURL) SetStaticMetadata(metadata resource.StaticMetadata) { + o.Name = metadata.Name + o.Namespace = metadata.Namespace + o.SetGroupVersionKind(schema.GroupVersionKind{ + Group: metadata.Group, + Version: metadata.Version, + Kind: metadata.Kind, + }) +} + +func (o *ShortURL) GetCommonMetadata() resource.CommonMetadata { + dt := o.DeletionTimestamp + var deletionTimestamp *time.Time + if dt != nil { + deletionTimestamp = &dt.Time + } + // Legacy ExtraFields support + extraFields := make(map[string]any) + if o.Annotations != nil { + extraFields["annotations"] = o.Annotations + } + if o.ManagedFields != nil { + extraFields["managedFields"] = o.ManagedFields + } + if o.OwnerReferences != nil { + extraFields["ownerReferences"] = o.OwnerReferences + } + return resource.CommonMetadata{ + UID: string(o.UID), + ResourceVersion: o.ResourceVersion, + Generation: o.Generation, + Labels: o.Labels, + CreationTimestamp: o.CreationTimestamp.Time, + DeletionTimestamp: deletionTimestamp, + Finalizers: o.Finalizers, + UpdateTimestamp: o.GetUpdateTimestamp(), + CreatedBy: o.GetCreatedBy(), + UpdatedBy: o.GetUpdatedBy(), + ExtraFields: extraFields, + } +} + +func (o *ShortURL) SetCommonMetadata(metadata resource.CommonMetadata) { + o.UID = types.UID(metadata.UID) + o.ResourceVersion = metadata.ResourceVersion + o.Generation = metadata.Generation + o.Labels = metadata.Labels + o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp) + if metadata.DeletionTimestamp != nil { + dt := metav1.NewTime(*metadata.DeletionTimestamp) + o.DeletionTimestamp = &dt + } else { + o.DeletionTimestamp = nil + } + o.Finalizers = metadata.Finalizers + if o.Annotations == nil { + o.Annotations = make(map[string]string) + } + if !metadata.UpdateTimestamp.IsZero() { + o.SetUpdateTimestamp(metadata.UpdateTimestamp) + } + if metadata.CreatedBy != "" { + o.SetCreatedBy(metadata.CreatedBy) + } + if metadata.UpdatedBy != "" { + o.SetUpdatedBy(metadata.UpdatedBy) + } + // Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields + if metadata.ExtraFields != nil { + if annotations, ok := metadata.ExtraFields["annotations"]; ok { + if cast, ok := annotations.(map[string]string); ok { + o.Annotations = cast + } + } + if managedFields, ok := metadata.ExtraFields["managedFields"]; ok { + if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok { + o.ManagedFields = cast + } + } + if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok { + if cast, ok := ownerReferences.([]metav1.OwnerReference); ok { + o.OwnerReferences = cast + } + } + } +} + +func (o *ShortURL) GetCreatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/createdBy"] +} + +func (o *ShortURL) SetCreatedBy(createdBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy +} + +func (o *ShortURL) GetUpdateTimestamp() time.Time { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"]) + return parsed +} + +func (o *ShortURL) SetUpdateTimestamp(updateTimestamp time.Time) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339) +} + +func (o *ShortURL) GetUpdatedBy() string { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + return o.ObjectMeta.Annotations["grafana.com/updatedBy"] +} + +func (o *ShortURL) SetUpdatedBy(updatedBy string) { + if o.ObjectMeta.Annotations == nil { + o.ObjectMeta.Annotations = make(map[string]string) + } + + o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy +} + +func (o *ShortURL) Copy() resource.Object { + return resource.CopyObject(o) +} + +func (o *ShortURL) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *ShortURL) DeepCopy() *ShortURL { + cpy := &ShortURL{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *ShortURL) DeepCopyInto(dst *ShortURL) { + dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion + dst.TypeMeta.Kind = o.TypeMeta.Kind + o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta) + o.Spec.DeepCopyInto(&dst.Spec) + o.Status.DeepCopyInto(&dst.Status) +} + +// Interface compliance compile-time check +var _ resource.Object = &ShortURL{} + +// +k8s:openapi-gen=true +type ShortURLList struct { + metav1.TypeMeta `json:",inline" yaml:",inline"` + metav1.ListMeta `json:"metadata" yaml:"metadata"` + Items []ShortURL `json:"items" yaml:"items"` +} + +func (o *ShortURLList) DeepCopyObject() runtime.Object { + return o.Copy() +} + +func (o *ShortURLList) Copy() resource.ListObject { + cpy := &ShortURLList{ + TypeMeta: o.TypeMeta, + Items: make([]ShortURL, len(o.Items)), + } + o.ListMeta.DeepCopyInto(&cpy.ListMeta) + for i := 0; i < len(o.Items); i++ { + if item, ok := o.Items[i].Copy().(*ShortURL); ok { + cpy.Items[i] = *item + } + } + return cpy +} + +func (o *ShortURLList) GetItems() []resource.Object { + items := make([]resource.Object, len(o.Items)) + for i := 0; i < len(o.Items); i++ { + items[i] = &o.Items[i] + } + return items +} + +func (o *ShortURLList) SetItems(items []resource.Object) { + o.Items = make([]ShortURL, len(items)) + for i := 0; i < len(items); i++ { + o.Items[i] = *items[i].(*ShortURL) + } +} + +func (o *ShortURLList) DeepCopy() *ShortURLList { + cpy := &ShortURLList{} + o.DeepCopyInto(cpy) + return cpy +} + +func (o *ShortURLList) DeepCopyInto(dst *ShortURLList) { + resource.CopyObjectInto(dst, o) +} + +// Interface compliance compile-time check +var _ resource.ListObject = &ShortURLList{} + +// Copy methods for all subresource types + +// DeepCopy creates a full deep copy of Spec +func (s *ShortURLSpec) DeepCopy() *ShortURLSpec { + cpy := &ShortURLSpec{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies Spec into another Spec object +func (s *ShortURLSpec) DeepCopyInto(dst *ShortURLSpec) { + resource.CopyObjectInto(dst, s) +} + +// DeepCopy creates a full deep copy of ShortURLStatus +func (s *ShortURLStatus) DeepCopy() *ShortURLStatus { + cpy := &ShortURLStatus{} + s.DeepCopyInto(cpy) + return cpy +} + +// DeepCopyInto deep copies ShortURLStatus into another ShortURLStatus object +func (s *ShortURLStatus) DeepCopyInto(dst *ShortURLStatus) { + resource.CopyObjectInto(dst, s) +} diff --git a/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_schema_gen.go b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_schema_gen.go new file mode 100644 index 00000000000..78f0d5839d2 --- /dev/null +++ b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_schema_gen.go @@ -0,0 +1,34 @@ +// +// Code generated by grafana-app-sdk. DO NOT EDIT. +// + +package v1alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" +) + +// schema is unexported to prevent accidental overwrites +var ( + schemaShortURL = resource.NewSimpleSchema("shorturl.grafana.app", "v1alpha1", &ShortURL{}, &ShortURLList{}, resource.WithKind("ShortURL"), + resource.WithPlural("shorturls"), resource.WithScope(resource.NamespacedScope)) + kindShortURL = resource.Kind{ + Schema: schemaShortURL, + Codecs: map[resource.KindEncoding]resource.Codec{ + resource.KindEncodingJSON: &ShortURLJSONCodec{}, + }, + } +) + +// Kind returns a resource.Kind for this Schema with a JSON codec +func ShortURLKind() resource.Kind { + return kindShortURL +} + +// Schema returns a resource.SimpleSchema representation of ShortURL +func ShortURLSchema() *resource.SimpleSchema { + return schemaShortURL +} + +// Interface compliance checks +var _ resource.Schema = kindShortURL diff --git a/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_spec_gen.go b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_spec_gen.go new file mode 100644 index 00000000000..56a453e5b69 --- /dev/null +++ b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_spec_gen.go @@ -0,0 +1,14 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v1alpha1 + +// +k8s:openapi-gen=true +type ShortURLSpec struct { + // The original path to where the short url is linking too e.g. https://localhost:3000/eer8i1kictngga/new-dashboard-with-lib-panel + Path string `json:"path"` +} + +// NewShortURLSpec creates a new ShortURLSpec object. +func NewShortURLSpec() *ShortURLSpec { + return &ShortURLSpec{} +} diff --git a/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_status_gen.go b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_status_gen.go new file mode 100644 index 00000000000..cfbeb15b338 --- /dev/null +++ b/apps/shorturl/pkg/apis/shorturl/v1alpha1/shorturl_status_gen.go @@ -0,0 +1,46 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v1alpha1 + +// +k8s:openapi-gen=true +type ShortURLstatusOperatorState struct { + // lastEvaluation is the ResourceVersion last evaluated + LastEvaluation string `json:"lastEvaluation"` + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + State ShortURLStatusOperatorStateState `json:"state"` + // descriptiveState is an optional more descriptive state field which has no requirements on format + DescriptiveState *string `json:"descriptiveState,omitempty"` + // details contains any extra information that is operator-specific + Details map[string]interface{} `json:"details,omitempty"` +} + +// NewShortURLstatusOperatorState creates a new ShortURLstatusOperatorState object. +func NewShortURLstatusOperatorState() *ShortURLstatusOperatorState { + return &ShortURLstatusOperatorState{} +} + +// +k8s:openapi-gen=true +type ShortURLStatus struct { + // The last time the short URL was used, 0 is the initial value + LastSeenAt int64 `json:"lastSeenAt"` + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + OperatorStates map[string]ShortURLstatusOperatorState `json:"operatorStates,omitempty"` + // additionalFields is reserved for future use + AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"` +} + +// NewShortURLStatus creates a new ShortURLStatus object. +func NewShortURLStatus() *ShortURLStatus { + return &ShortURLStatus{} +} + +// +k8s:openapi-gen=true +type ShortURLStatusOperatorStateState string + +const ( + ShortURLStatusOperatorStateStateSuccess ShortURLStatusOperatorStateState = "success" + ShortURLStatusOperatorStateStateInProgress ShortURLStatusOperatorStateState = "in_progress" + ShortURLStatusOperatorStateStateFailed ShortURLStatusOperatorStateState = "failed" +) diff --git a/apps/shorturl/pkg/apis/shorturl_manifest.go b/apps/shorturl/pkg/apis/shorturl_manifest.go new file mode 100644 index 00000000000..5cb4b1b2d07 --- /dev/null +++ b/apps/shorturl/pkg/apis/shorturl_manifest.go @@ -0,0 +1,75 @@ +// +// This file is generated by grafana-app-sdk +// DO NOT EDIT +// + +package apis + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/resource" + + v1alpha1 "github.com/grafana/grafana/apps/shorturl/pkg/apis/shorturl/v1alpha1" +) + +var ( + rawSchemaShortURLv1alpha1 = []byte(`{"spec":{"properties":{"path":{"description":"The original path to where the short url is linking too e.g. https://localhost:3000/eer8i1kictngga/new-dashboard-with-lib-panel","type":"string"}},"required":["path"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastSeenAt":{"description":"The last time the short URL was used, 0 is the initial value","format":"int64","type":"integer"},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"required":["lastSeenAt"],"type":"object"}}`) + versionSchemaShortURLv1alpha1 app.VersionSchema + _ = json.Unmarshal(rawSchemaShortURLv1alpha1, &versionSchemaShortURLv1alpha1) +) + +var appManifestData = app.ManifestData{ + AppName: "shorturl", + Group: "shorturl.grafana.app", + Versions: []app.ManifestVersion{ + { + Name: "v1alpha1", + Served: true, + Kinds: []app.ManifestVersionKind{ + { + Kind: "ShortURL", + Plural: "ShortURLs", + Scope: "Namespaced", + Conversion: false, + Schema: &versionSchemaShortURLv1alpha1, + }, + }, + }, + }, +} + +func LocalManifest() app.Manifest { + return app.NewEmbeddedManifest(appManifestData) +} + +func RemoteManifest() app.Manifest { + return app.NewAPIServerManifest("shorturl") +} + +var kindVersionToGoType = map[string]resource.Kind{ + "ShortURL/v1alpha1": v1alpha1.ShortURLKind(), +} + +// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists. +// If there is no association for the provided Kind and Version, exists will return false. +func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) { + goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)] + return goType, exists +} + +var customRouteToGoResponseType = map[string]any{} + +// ManifestCustomRouteResponsesAssociator returns the associated response go type for a given kind, version, custom route path, and method, if one exists. +// kind may be empty for custom routes which are not kind subroutes. Leading slashes are removed from subroute paths. +// If there is no association for the provided kind, version, custom route path, and method, exists will return false. +func ManifestCustomRouteResponsesAssociator(kind, version, path, verb string) (goType any, exists bool) { + if len(path) > 0 && path[0] == '/' { + path = path[1:] + } + goType, exists = customRouteToGoResponseType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))] + return goType, exists +} diff --git a/apps/shorturl/pkg/app/app.go b/apps/shorturl/pkg/app/app.go new file mode 100644 index 00000000000..519ebd5f187 --- /dev/null +++ b/apps/shorturl/pkg/app/app.go @@ -0,0 +1,69 @@ +package app + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/klog/v2" + + "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/resource" + "github.com/grafana/grafana-app-sdk/simple" + shorturlv1alpha1 "github.com/grafana/grafana/apps/shorturl/pkg/apis/shorturl/v1alpha1" +) + +// Local error definitions to avoid importing the main shorturls package +var ( + ErrShortURLAbsolutePath = fmt.Errorf("path should be relative") + ErrShortURLInvalidPath = fmt.Errorf("invalid short URL path") +) + +type ShortURLConfig struct { + AppURL string +} + +func New(cfg app.Config) (app.App, error) { + // Extract the AppURL from the specific config + shortURLConfig, ok := cfg.SpecificConfig.(*ShortURLConfig) + if !ok || shortURLConfig == nil { + return nil, fmt.Errorf("invalid or missing ShortURLConfig") + } + + simpleConfig := simple.AppConfig{ + Name: "shorturl", + KubeConfig: cfg.KubeConfig, + InformerConfig: simple.AppInformerConfig{ + ErrorHandler: func(ctx context.Context, err error) { + klog.ErrorS(err, "Informer processing error") + }, + }, + ManagedKinds: []simple.AppManagedKind{ + { + Kind: shorturlv1alpha1.ShortURLKind(), + }, + }, + } + + a, err := simple.NewApp(simpleConfig) + if err != nil { + return nil, err + } + + err = a.ValidateManifest(cfg.ManifestData) + if err != nil { + return nil, err + } + + return a, nil +} + +func GetKinds() map[schema.GroupVersion][]resource.Kind { + gv := schema.GroupVersion{ + Group: shorturlv1alpha1.ShortURLKind().Group(), + Version: shorturlv1alpha1.ShortURLKind().Version(), + } + return map[schema.GroupVersion][]resource.Kind{ + gv: {shorturlv1alpha1.ShortURLKind()}, + } +} diff --git a/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/shorturl_object_gen.ts b/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/shorturl_object_gen.ts new file mode 100644 index 00000000000..3c4fe0bd088 --- /dev/null +++ b/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/shorturl_object_gen.ts @@ -0,0 +1,49 @@ +/* + * This file was generated by grafana-app-sdk. DO NOT EDIT. + */ +import { Spec } from './types.spec.gen'; +import { Status } from './types.status.gen'; + +export interface Metadata { + name: string; + namespace: string; + generateName?: string; + selfLink?: string; + uid?: string; + resourceVersion?: string; + generation?: number; + creationTimestamp?: string; + deletionTimestamp?: string; + deletionGracePeriodSeconds?: number; + labels?: Record; + annotations?: Record; + ownerReferences?: OwnerReference[]; + finalizers?: string[]; + managedFields?: ManagedFieldsEntry[]; +} + +export interface OwnerReference { + apiVersion: string; + kind: string; + name: string; + uid: string; + controller?: boolean; + blockOwnerDeletion?: boolean; +} + +export interface ManagedFieldsEntry { + manager?: string; + operation?: string; + apiVersion?: string; + time?: string; + fieldsType?: string; + subresource?: string; +} + +export interface ShortURL { + kind: string; + apiVersion: string; + metadata: Metadata; + spec: Spec; + status: Status; +} diff --git a/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/types.metadata.gen.ts b/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/types.metadata.gen.ts new file mode 100644 index 00000000000..4377f3c1d08 --- /dev/null +++ b/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/types.metadata.gen.ts @@ -0,0 +1,30 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +// metadata contains embedded CommonMetadata and can be extended with custom string fields +// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here +// without external reference as using the CommonMetadata reference breaks thema codegen. +export interface Metadata { + updateTimestamp: string; + createdBy: string; + uid: string; + creationTimestamp: string; + deletionTimestamp?: string; + finalizers: string[]; + resourceVersion: string; + generation: number; + updatedBy: string; + labels: Record; +} + +export const defaultMetadata = (): Metadata => ({ + updateTimestamp: "", + createdBy: "", + uid: "", + creationTimestamp: "", + finalizers: [], + resourceVersion: "", + generation: 0, + updatedBy: "", + labels: {}, +}); + diff --git a/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/types.spec.gen.ts b/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/types.spec.gen.ts new file mode 100644 index 00000000000..5c639fedf89 --- /dev/null +++ b/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/types.spec.gen.ts @@ -0,0 +1,11 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +export interface Spec { + // The original path to where the short url is linking too e.g. https://localhost:3000/eer8i1kictngga/new-dashboard-with-lib-panel + path: string; +} + +export const defaultSpec = (): Spec => ({ + path: "", +}); + diff --git a/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/types.status.gen.ts b/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/types.status.gen.ts new file mode 100644 index 00000000000..c67940d5cab --- /dev/null +++ b/apps/shorturl/plugin/src/generated/shorturl/v1alpha1/types.status.gen.ts @@ -0,0 +1,33 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +export interface OperatorState { + // lastEvaluation is the ResourceVersion last evaluated + lastEvaluation: string; + // state describes the state of the lastEvaluation. + // It is limited to three possible states for machine evaluation. + state: "success" | "in_progress" | "failed"; + // descriptiveState is an optional more descriptive state field which has no requirements on format + descriptiveState?: string; + // details contains any extra information that is operator-specific + details?: Record; +} + +export const defaultOperatorState = (): OperatorState => ({ + lastEvaluation: "", + state: "success", +}); + +export interface Status { + // The last time the short URL was used, 0 is the initial value + lastSeenAt: number; + // operatorStates is a map of operator ID to operator state evaluations. + // Any operator which consumes this kind SHOULD add its state evaluation information to this field. + operatorStates?: Record; + // additionalFields is reserved for future use + additionalFields?: Record; +} + +export const defaultStatus = (): Status => ({ + lastSeenAt: 0, +}); + diff --git a/conf/defaults.ini b/conf/defaults.ini index 854d348e8c9..be7e06d7223 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -200,6 +200,10 @@ transaction_retries = 5 # Set to true to add metrics and tracing for database queries. instrument_queries = false +# Set to true to delete auto-generated primary keys during migrations. +# This is useful when databases have auto-generated primary keys enabled. +delete_auto_gen_ids = false + #################################### Cache server ############################# [remote_cache] # Either "redis", "memcached" or "database" default is "database" diff --git a/conf/sample.ini b/conf/sample.ini index 30e5bb42f6a..6d3afd69292 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -197,6 +197,10 @@ # Set to true to add metrics and tracing for database queries. ;instrument_queries = false +# Set to true to delete auto-generated primary keys during migrations. +# This is useful when databases have auto-generated primary keys enabled. +;delete_auto_gen_ids = false + #################################### Cache server ############################# [remote_cache] # Either "redis", "memcached" or "database" default is "database" diff --git a/devenv/README.md b/devenv/README.md index cc37e031d0a..72c218037b9 100644 --- a/devenv/README.md +++ b/devenv/README.md @@ -14,53 +14,57 @@ Grafana uses [Docker](https://docker.com) to make the task of setting up databas ## Developer dashboards and data sources To setup developer dashboards and data sources + ```bash ./setup.sh ``` To remove the setup developer dashboards and data sources + ```bash ./setup.sh undev ``` After restarting the Grafana server, there should be a number of data sources named `gdev-` provisioned as well as -a dashboard folder named `gdev dashboards`. This folder contains dashboard and panel features tests dashboards. +a dashboard folder named `gdev dashboards`. This folder contains dashboard and panel features tests dashboards. Please update these dashboards or make new ones as new panels and dashboards features are developed or new bugs are -found. The dashboards are located in the `devenv/dev-dashboards` folder. +found. The dashboards are located in the `devenv/dev-dashboards` folder. ## docker-compose with databases This command creates a docker-compose file with specified databases configured and ready to run. Each database has a prepared image with some fake data ready to use. For available databases, see `docker/blocks` directory. Notice that -for some databases there are multiple images with different versions. Some blocks such as `slow_proxy_mac` or `apache_proxy_mac` are specifically for Macs. +for some databases there are multiple images with different versions. Some blocks such as `slow_proxy_mac` or `apache_proxy_mac` are specifically for Macs. ```bash make devenv sources=influxdb,prometheus,elastic5 ``` -Some of the blocks support dynamic change of the image version used in the Docker file. The signature looks like this: +Some of the blocks support dynamic change of the image version used in the Docker file. The signature looks like this: ```bash make devenv sources=postgres,auth/openldap,grafana postgres_version=9.2 grafana_version=6.7.0-beta1 ``` - ### Notes per block #### Grafana + The grafana block is pre-configured with the dev-datasources and dashboards. #### Tempo + The tempo block runs loki and prometheus as well and should not be ran with prometheus as a separate source. You need to install a docker plugin for the self logging to work, without it the container won't start. See https://grafana.com/docs/loki/latest/clients/docker-driver/#installing for installation instructions. #### Jaeger + Jaeger block runs both Jaeger and Loki container. Loki container sends traces to Jaeger and also logs its own logs into itself so it is possible to setup derived field for traceID from Loki to Jaeger. You need to install a docker plugin for the self logging to work, without it the container won't start. See https://grafana.com/docs/loki/latest/clients/docker-driver/#installing for installation instructions. #### Graphite | version | source name | graphite-web port | plaintext port | pickle port | -|---------|-------------|-------------------|----------------|-------------| +| ------- | ----------- | ----------------- | -------------- | ----------- | | 1.1 | graphite | 8180 | 2103 | 2103 | | 1.0 | graphite1 | 8280 | 2203 | 2203 | | 0.9 | graphite09 | 8380 | 2303 | 2303 | @@ -81,7 +85,8 @@ host = "localhost:1025" You can access the web UI at http://localhost:12080/#/ ## Debugging setup in VS Code -An example of launch.json is provided in `.vscode/launch.json`. It basically does what Makefile and .bra.toml do. The 'program' field is set to the folder name so VS Code loads all *.go files in it instead of just main.go. + +An example of launch.json is provided in `.vscode/launch.json`. It basically does what Makefile and .bra.toml do. The 'program' field is set to the folder name so VS Code loads all \*.go files in it instead of just main.go. ## Troubleshooting diff --git a/devenv/datasources.yaml b/devenv/datasources.yaml index 8eaad77a2fb..346cd22e02d 100644 --- a/devenv/datasources.yaml +++ b/devenv/datasources.yaml @@ -27,21 +27,21 @@ datasources: access: proxy url: http://localhost:8180 jsonData: - graphiteVersion: "1.1" + graphiteVersion: '1.1' - name: gdev-graphite-1.0 type: graphite access: proxy url: http://localhost:8280 jsonData: - graphiteVersion: "1.0" + graphiteVersion: '1.0' - name: gdev-graphite-0.9 type: graphite access: proxy url: http://localhost:8380 jsonData: - graphiteVersion: "0.9" + graphiteVersion: '0.9' - name: gdev-alertmanager type: alertmanager @@ -64,8 +64,8 @@ datasources: prometheusType: Prometheus #Cortex | Mimir | Prometheus | Thanos prometheusVersion: 2.40.0 exemplarTraceIdDestinations: - - name: traceID - datasourceUid: gdev-tempo + - name: traceID + datasourceUid: gdev-tempo secureJsonData: basicAuthPassword: admin #https://grafana.com/docs/grafana/latest/administration/provisioning/#using-environment-variables @@ -84,6 +84,7 @@ datasources: basicAuthPassword: admin #https://grafana.com/docs/grafana/latest/administration/provisioning/#using-environment-variables - name: gdev-testdata + uid: PD8C576611E62080A isDefault: true type: testdata @@ -116,9 +117,9 @@ datasources: url: http://localhost:8086 jsonData: dbName: mybucket - httpHeaderName1: "Authorization" + httpHeaderName1: 'Authorization' secureJsonData: - httpHeaderValue1: "Token mytoken" + httpHeaderValue1: 'Token mytoken' - name: gdev-opentsdb type: opentsdb @@ -150,9 +151,9 @@ datasources: access: proxy url: http://localhost:9200 jsonData: - index: "[logs-]YYYY.MM.DD" + index: '[logs-]YYYY.MM.DD' interval: Daily - timeField: "@timestamp" + timeField: '@timestamp' logLevelField: level logMessageField: line @@ -161,10 +162,10 @@ datasources: access: proxy url: http://localhost:9200 jsonData: - index: "[filebeat-]YYYY.MM.DD" + index: '[filebeat-]YYYY.MM.DD' interval: Daily - timeField: "@timestamp" - timeInterval: "10s" + timeField: '@timestamp' + timeInterval: '10s' logMessageField: message logLevelField: fields.level @@ -173,10 +174,10 @@ datasources: access: proxy url: http://localhost:9200 jsonData: - index: "[metricbeat-]YYYY.MM.DD" + index: '[metricbeat-]YYYY.MM.DD' interval: Daily - timeField: "@timestamp" - timeInterval: "10s" + timeField: '@timestamp' + timeInterval: '10s' - name: gdev-mysql type: mysql @@ -211,7 +212,7 @@ datasources: user: grafana jsonData: database: grafana - encrypt: "true" + encrypt: 'true' tlsSkipVerify: true secureJsonData: password: Password! @@ -232,7 +233,7 @@ datasources: secureJsonData: password: password jsonData: - sslmode: "disable" + sslmode: 'disable' database: grafana - name: gdev-postgres-ds-tests @@ -242,7 +243,7 @@ datasources: secureJsonData: password: grafanatest jsonData: - sslmode: "disable" + sslmode: 'disable' database: grafanadstest - name: gdev-cloudwatch @@ -252,7 +253,7 @@ datasources: jsonData: authType: credentials defaultRegion: eu-west-2 - customMetricsNamespaces: "CWAgent" + customMetricsNamespaces: 'CWAgent' - name: gdev-loki uid: gdev-loki @@ -262,36 +263,36 @@ datasources: editable: false correlations: - targetUID: gdev-jaeger - label: "Jaeger traces" - description: "Related traces stored in Jaeger" + label: 'Jaeger traces' + description: 'Related traces stored in Jaeger' - targetUID: gdev-zipkin - label: "Zipkin traces" - description: "Related traces stored in Zipkin" + label: 'Zipkin traces' + description: 'Related traces stored in Zipkin' - targetUID: gdev-tempo - label: "Tempo traces" - description: "Related traces stored in Tempo" + label: 'Tempo traces' + description: 'Related traces stored in Tempo' - targetUID: gdev-prometheus - label: "Logs to metrics" - description: "Related metrics stored in Prometheus" + label: 'Logs to metrics' + description: 'Related metrics stored in Prometheus' config: type: query target: - expr: "{ job=\"test\" }" - field: "traceID" + expr: '{ job="test" }' + field: 'traceID' jsonData: manageAlerts: false derivedFields: - - name: "traceID" + - name: 'traceID' matcherRegex: "traceID=(\\w+)" - url: "$${__value.raw}" + url: '$${__value.raw}' datasourceUid: gdev-jaeger - - name: "traceID" + - name: 'traceID' matcherRegex: "traceID=(\\w+)" - url: "$${__value.raw}" + url: '$${__value.raw}' datasourceUid: gdev-zipkin - - name: "traceID" + - name: 'traceID' matcherRegex: "traceID=(\\w+)" - url: "$${__value.raw}" + url: '$${__value.raw}' datasourceUid: gdev-tempo - name: gdev-jaeger @@ -316,23 +317,23 @@ datasources: editable: false correlations: - targetUID: gdev-loki - label: "Logs (correlation)" - description: "Correlation to logs stored in Loki" + label: 'Logs (correlation)' + description: 'Correlation to logs stored in Loki' config: type: query target: - expr: "{ job=\"job\" }" - field: "traceID" + expr: '{ job="job" }' + field: 'traceID' jsonData: tracesToLogsV2: - datasourceUid: gdev-loki - spanStartTimeShift: '5m' - spanEndTimeShift: '-5m' - customQuery: true - query: '{filename="/var/log/grafana/grafana.log"} |="$${__span.traceId}"' + datasourceUid: gdev-loki + spanStartTimeShift: '5m' + spanEndTimeShift: '-5m' + customQuery: true + query: '{filename="/var/log/grafana/grafana.log"} |="$${__span.traceId}"' tracesToProfiles: datasourceUid: gdev-pyroscope - profileTypeId: "process_cpu:cpu:nanoseconds:cpu:nanoseconds" + profileTypeId: 'process_cpu:cpu:nanoseconds:cpu:nanoseconds' tracesToMetrics: datasourceUid: gdev-prometheus spanStartTimeShift: '1h' @@ -352,7 +353,6 @@ datasources: url: http://localhost:4040 editable: false - - name: gdev-e2etestdatasource type: grafana-e2etest-datasource uid: gdev-e2etest-datasource diff --git a/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json b/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json index 98d49958aaf..9f2fce99582 100644 --- a/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json +++ b/devenv/dev-dashboards-without-uid/panel_tests_graph_time_regions.json @@ -467,10 +467,7 @@ ], "refresh": false, "schemaVersion": 16, - "tags": [ - "gdev", - "panel-tests" - ], + "tags": ["gdev", "panel-tests"], "templating": { "list": [] }, @@ -479,20 +476,9 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "browser", "title": "Panel Tests - Graph (Time Regions)", "version": 1 -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards-without-uid/panel_tests_polystat.json b/devenv/dev-dashboards-without-uid/panel_tests_polystat.json index 25b1f7154e5..d70f9ee68cc 100644 --- a/devenv/dev-dashboards-without-uid/panel_tests_polystat.json +++ b/devenv/dev-dashboards-without-uid/panel_tests_polystat.json @@ -43,48 +43,8 @@ } ], "fontSizes": [ - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 22, - 24, - 26, - 28, - 30, - 32, - 34, - 36, - 38, - 40, - 42, - 44, - 46, - 48, - 50, - 52, - 54, - 56, - 58, - 60, - 62, - 64, - 66, - 68, - 70 + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, + 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70 ], "fontTypes": [ "Open Sans", @@ -1201,48 +1161,8 @@ } ], "fontSizes": [ - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 22, - 24, - 26, - 28, - 30, - 32, - 34, - 36, - 38, - 40, - 42, - 44, - 46, - 48, - 50, - 52, - 54, - 56, - 58, - 60, - 62, - 64, - 66, - 68, - 70 + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, + 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70 ], "fontTypes": [ "Open Sans", @@ -2324,48 +2244,8 @@ } ], "fontSizes": [ - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 22, - 24, - 26, - 28, - 30, - 32, - 34, - 36, - 38, - 40, - 42, - 44, - 46, - 48, - 50, - 52, - 54, - 56, - 58, - 60, - 62, - 64, - 66, - 68, - 70 + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, + 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70 ], "fontTypes": [ "Open Sans", diff --git a/devenv/dev-dashboards/all-panels.json b/devenv/dev-dashboards/all-panels.json index c6d93b191fc..873691e4c8d 100644 --- a/devenv/dev-dashboards/all-panels.json +++ b/devenv/dev-dashboards/all-panels.json @@ -1,981 +1,967 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, + "annotations": { + "list": [ + { + "builtIn": 1, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], "type": "dashboard" - } - ] - }, - "editable": true, - "graphTooltip": 0, - "iteration": 1629464729438, - "links": [ - { - "asDropdown": true, - "icon": "external link", - "includeVars": false, - "keepTime": false, - "tags": [ - "gdev", - "panel-tests" - ], - "targetBlank": false, - "title": "Dropdown link", - "tooltip": "", - "type": "dashboards", - "url": "" - }, - { - "asDropdown": false, - "icon": "external link", - "includeVars": false, - "keepTime": false, - "tags": [], - "targetBlank": false, - "title": "External link", - "tooltip": "localhost", - "type": "link", - "url": "localhost:3000" + }, + "type": "dashboard" } - ], - "panels": [ - { - "gridPos": { - "h": 3, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 34, - "options": { - "content": "# All panels\n\nThis dashboard was created to quickly check accessiblity issues on a lot of panels at the same time ", - "mode": "markdown" - }, - "pluginVersion": "8.1.0-pre", - "transparent": true, - "type": "text" + ] + }, + "editable": true, + "graphTooltip": 0, + "iteration": 1629464729438, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": false, + "keepTime": false, + "tags": ["gdev", "panel-tests"], + "targetBlank": false, + "title": "Dropdown link", + "tooltip": "", + "type": "dashboards", + "url": "" + }, + { + "asDropdown": false, + "icon": "external link", + "includeVars": false, + "keepTime": false, + "tags": [], + "targetBlank": false, + "title": "External link", + "tooltip": "localhost", + "type": "link", + "url": "localhost:3000" + } + ], + "panels": [ + { + "gridPos": { + "h": 3, + "w": 12, + "x": 0, + "y": 0 }, - { - "gridPos": { - "h": 3, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 35, - "options": { - "content": "# Another text panel\n\nBecause why not", - "mode": "markdown" - }, - "pluginVersion": "8.1.0-pre", - "transparent": true, - "type": "text" + "id": 34, + "options": { + "content": "# All panels\n\nThis dashboard was created to quickly check accessiblity issues on a lot of panels at the same time ", + "mode": "markdown" }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 3 - }, - "id": 32, - "panels": [], - "title": "Row title", - "type": "row" + "pluginVersion": "8.1.0-pre", + "transparent": true, + "type": "text" + }, + { + "gridPos": { + "h": 3, + "w": 12, + "x": 12, + "y": 0 }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "fillOpacity": 70, - "lineWidth": 1 - }, - "mappings": [ - { - "options": { - "CRITICAL": { - "color": "red", - "index": 3 - }, - "HIGH": { - "color": "orange", - "index": 2 - }, - "LOW": { - "color": "blue", - "index": 0 - }, - "NORMAL": { - "color": "green", - "index": 1 - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 24, - "x": 0, - "y": 4 - }, - "id": 41, - "options": { - "alignValue": "left", - "legend": { - "displayMode": "list", - "placement": "bottom" - }, - "mergeValues": true, - "rowHeight": 0.99, - "showValue": "auto", - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "refId": "A", - "scenarioId": "csv_metric_values", - "stringInput": "LOW,HIGH,NORMAL,NORMAL,NORMAL,LOW,LOW,NORMAL,HIGH,CRITICAL" - }, - { - "refId": "B", - "scenarioId": "csv_metric_values", - "stringInput": "NORMAL,LOW,LOW,CRITICAL,CRITICAL,LOW,LOW,NORMAL,HIGH,CRITICAL" - }, - { - "refId": "C", - "scenarioId": "csv_metric_values", - "stringInput": "NORMAL,NORMAL,NORMAL,NORMAL,CRITICAL,LOW,NORMAL,NORMAL,NORMAL,LOW" - } - ], - "title": "State timeline", - "type": "state-timeline" + "id": 35, + "options": { + "content": "# Another text panel\n\nBecause why not", + "mode": "markdown" }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "continuous-GrYlRd" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } + "pluginVersion": "8.1.0-pre", + "transparent": true, + "type": "text" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 3 + }, + "id": 32, + "panels": [], + "title": "Row title", + "type": "row" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" }, - "overrides": [] - }, - "gridPos": { - "h": 11, - "w": 9, - "x": 0, - "y": 0 - }, - "id": 62, - "options": { - "basemap": { - "config": {}, - "type": "default" + "custom": { + "fillOpacity": 70, + "lineWidth": 1 }, - "controls": { - "mouseWheelZoom": true, - "showAttribution": true, - "showDebug": false, - "showScale": false, - "showZoom": true - }, - "layers": [ + "mappings": [ { - "config": { - "color": { - "field": "Price", - "fixed": "dark-green" + "options": { + "CRITICAL": { + "color": "red", + "index": 3 }, - "fillOpacity": 0.4, - "shape": "circle", - "showLegend": true, - "size": { - "field": "Count", - "fixed": 5, - "max": 15, - "min": 2 + "HIGH": { + "color": "orange", + "index": 2 + }, + "LOW": { + "color": "blue", + "index": 0 + }, + "NORMAL": { + "color": "green", + "index": 1 } }, - "location": { - "gazetteer": "public/gazetteer/usa-states.json", - "lookup": "State", - "mode": "auto" - }, - "type": "markers" + "type": "value" } ], - "view": { - "id": "coords", - "lat": 38.297683, - "lon": -99.228359, - "shared": true, - "zoom": 3.98 - } - }, - "targets": [ - { - "csvFileName": "flight_info_by_state.csv", - "refId": "A", - "scenarioId": "csv_file" - } - ], - "title": "Size, color mapped to different fields + share view", - "type": "geomap" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "fillOpacity": 80, - "gradientMode": "hue", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1 - }, - "links": [ + "thresholds": { + "mode": "absolute", + "steps": [ { - "title": "Data link", - "url": "/" + "color": "green" + }, + { + "color": "red", + "value": 80 } - ], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 13 - }, - "id": 4, - "options": { - "bucketOffset": 0, - "combine": false, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" + ] } }, - "title": "Histogram", - "type": "histogram" + "overrides": [] }, - { - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 21 - }, - "id": 28, - "options": { - "dedupStrategy": "none", - "enableLogDetails": true, - "prettifyLogMessage": false, - "showCommonLabels": false, - "showLabels": false, - "showTime": false, - "sortOrder": "Descending", - "wrapLogMessage": false - }, - "targets": [ - { - "refId": "A", - "scenarioId": "logs" - } - ], - "title": "Logs", - "type": "logs" + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 4 }, - { - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 21 - }, - "id": 8, - "options": { - "maxItems": 10, - "query": "", - "showHeadings": true, - "showRecentlyViewed": true, - "showSearch": true, - "showStarred": true, - "tags": [] - }, - "pluginVersion": "8.1.0-pre", - "title": "Dashboard list", - "type": "dashlist" - }, - { - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 29 - }, - "id": 6, - "options": { - "alertName": "", - "dashboardAlerts": false, - "maxItems": 20, - "showInstances": false, - "sortOrder": 1, - "stateFilter": { - "firing": true, - "inactive": false, - "pending": true - } - }, - "title": "Alert list", - "type": "alertlist" - }, - { - "cards": { - }, - "color": { - "cardColor": "#b4ff00", - "colorScale": "sqrt", - "colorScheme": "interpolateOranges", - "exponent": 0.5, - "mode": "spectrum" - }, - "dataFormat": "timeseries", - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 37 - }, - "heatmap": {}, - "hideZeroBuckets": false, - "highlightCards": true, - "id": 26, + "id": 41, + "options": { + "alignValue": "left", "legend": { - "show": false + "displayMode": "list", + "placement": "bottom" }, - "reverseYBuckets": false, - "targets": [ - { - "refId": "A", - "scenarioId": "exponential_heatmap_bucket_data" - } - ], - "title": "Heatmap", + "mergeValues": true, + "rowHeight": 0.99, + "showValue": "auto", "tooltip": { - "show": true, - "showHistogram": false - }, - "type": "heatmap", - "xAxis": { - "show": true - }, - "yAxis": { - "format": "short", - "logBase": 1, - "show": true - }, - "yBucketBound": "auto" + "mode": "single", + "sort": "none" + } }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 37 - }, - "id": 20, - "options": { - "displayMode": "gradient", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showUnfilled": true, - "text": {} - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "refId": "A", - "scenarioId": "random_walk", - "seriesCount": 10 - } - ], - "title": "Bar gauge", - "type": "bargauge" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - } - }, - "mappings": [] - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 45 - }, - "id": 24, - "options": { - "legend": { - "displayMode": "list", - "placement": "bottom" - }, - "pieType": "pie", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "refId": "A", - "scenarioId": "random_walk", - "seriesCount": 6 - } - ], - "title": "Pie chart", - "type": "piechart" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 45 - }, - "id": 18, - "options": { - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "orientation": "auto", - "showValue": "auto", - "showThresholdLabels": false, - "showThresholdMarkers": true, - "text": {} - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "refId": "A", - "scenarioId": "random_walk", - "seriesCount": 3 - } - ], - "title": "Gauge", - "type": "gauge" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 53 - }, - "id": 22, - "options": { - "showHeader": true - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "refId": "A", - "scenarioId": "table_static" - } - ], - "title": "Tabel", - "type": "table" - }, - { - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 53 - }, - "id": 10, - "options": { - "limit": 10, - "navigateAfter": "10m", - "navigateBefore": "10m", - "onlyFromThisDashboard": false, - "onlyInTimeRange": false, - "showTags": true, - "showTime": true, - "showUser": true - }, - "title": "Annotation list", - "type": "annolist" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 61 - }, - "id": 16, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "8.1.0-pre", - "title": "Stat", - "type": "stat" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 61 - }, - "id": 2, - "links": [], - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "refId": "A", - "scenarioId": "random_walk", - "seriesCount": 4, - "spread": 120 - } - ], - "title": "Graph NG", - "type": "timeseries" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1 - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 69 - }, - "id": 14, - "options": { - "barWidth": 0.97, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom" - }, - "orientation": "auto", - "showValue": "auto", - "stacking": "none", - "text": {}, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "csvContent": "Series, Value\nBar 2, 20\nBar 3, 25\nBar 3, 55.4", - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Bar chart", - "type": "barchart" - }, - { - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 77 - }, - "id": 12, - "options": { - "showImage": true - }, - "title": "News panel", - "type": "news" - } - ], - "refresh": "", - "schemaVersion": 36, - "tags": [ - "gdev", - "panel-tests", - "all-panels" - ], - "templating": { - "list": [ + "targets": [ { - "current": {}, - "datasource": "gdev-testdata", - "definition": "*", - "hide": 0, - "includeAll": true, - "multi": true, - "name": "query0", - "options": [], - "query": { - "query": "*", - "refId": "StandardVariableQuery" - }, - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "type": "query" + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "LOW,HIGH,NORMAL,NORMAL,NORMAL,LOW,LOW,NORMAL,HIGH,CRITICAL" }, { - "current": { + "refId": "B", + "scenarioId": "csv_metric_values", + "stringInput": "NORMAL,LOW,LOW,CRITICAL,CRITICAL,LOW,LOW,NORMAL,HIGH,CRITICAL" + }, + { + "refId": "C", + "scenarioId": "csv_metric_values", + "stringInput": "NORMAL,NORMAL,NORMAL,NORMAL,CRITICAL,LOW,NORMAL,NORMAL,NORMAL,LOW" + } + ], + "title": "State timeline", + "type": "state-timeline" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 9, + "x": 0, + "y": 0 + }, + "id": 62, + "options": { + "basemap": { + "config": {}, + "type": "default" + }, + "controls": { + "mouseWheelZoom": true, + "showAttribution": true, + "showDebug": false, + "showScale": false, + "showZoom": true + }, + "layers": [ + { + "config": { + "color": { + "field": "Price", + "fixed": "dark-green" + }, + "fillOpacity": 0.4, + "shape": "circle", + "showLegend": true, + "size": { + "field": "Count", + "fixed": 5, + "max": 15, + "min": 2 + } + }, + "location": { + "gazetteer": "public/gazetteer/usa-states.json", + "lookup": "State", + "mode": "auto" + }, + "type": "markers" + } + ], + "view": { + "id": "coords", + "lat": 38.297683, + "lon": -99.228359, + "shared": true, + "zoom": 3.98 + } + }, + "targets": [ + { + "csvFileName": "flight_info_by_state.csv", + "refId": "A", + "scenarioId": "csv_file" + } + ], + "title": "Size, color mapped to different fields + share view", + "type": "geomap" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "fillOpacity": 80, + "gradientMode": "hue", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1 + }, + "links": [ + { + "title": "Data link", + "url": "/" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 13 + }, + "id": 4, + "options": { + "bucketOffset": 0, + "combine": false, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + } + }, + "title": "Histogram", + "type": "histogram" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 21 + }, + "id": 28, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": false, + "showLabels": false, + "showTime": false, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "targets": [ + { + "refId": "A", + "scenarioId": "logs" + } + ], + "title": "Logs", + "type": "logs" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 21 + }, + "id": 8, + "options": { + "maxItems": 10, + "query": "", + "showHeadings": true, + "showRecentlyViewed": true, + "showSearch": true, + "showStarred": true, + "tags": [] + }, + "pluginVersion": "8.1.0-pre", + "title": "Dashboard list", + "type": "dashlist" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 29 + }, + "id": 6, + "options": { + "alertName": "", + "dashboardAlerts": false, + "maxItems": 20, + "showInstances": false, + "sortOrder": 1, + "stateFilter": { + "firing": true, + "inactive": false, + "pending": true + } + }, + "title": "Alert list", + "type": "alertlist" + }, + { + "cards": {}, + "color": { + "cardColor": "#b4ff00", + "colorScale": "sqrt", + "colorScheme": "interpolateOranges", + "exponent": 0.5, + "mode": "spectrum" + }, + "dataFormat": "timeseries", + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 37 + }, + "heatmap": {}, + "hideZeroBuckets": false, + "highlightCards": true, + "id": 26, + "legend": { + "show": false + }, + "reverseYBuckets": false, + "targets": [ + { + "refId": "A", + "scenarioId": "exponential_heatmap_bucket_data" + } + ], + "title": "Heatmap", + "tooltip": { + "show": true, + "showHistogram": false + }, + "type": "heatmap", + "xAxis": { + "show": true + }, + "yAxis": { + "format": "short", + "logBase": 1, + "show": true + }, + "yBucketBound": "auto" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 37 + }, + "id": 20, + "options": { + "displayMode": "gradient", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showUnfilled": true, + "text": {} + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 10 + } + ], + "title": "Bar gauge", + "type": "bargauge" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 45 + }, + "id": 24, + "options": { + "legend": { + "displayMode": "list", + "placement": "bottom" + }, + "pieType": "pie", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 6 + } + ], + "title": "Pie chart", + "type": "piechart" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 45 + }, + "id": 18, + "options": { + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "orientation": "auto", + "showValue": "auto", + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 3 + } + ], + "title": "Gauge", + "type": "gauge" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 53 + }, + "id": 22, + "options": { + "showHeader": true + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "table_static" + } + ], + "title": "Tabel", + "type": "table" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 53 + }, + "id": 10, + "options": { + "limit": 10, + "navigateAfter": "10m", + "navigateBefore": "10m", + "onlyFromThisDashboard": false, + "onlyInTimeRange": false, + "showTags": true, + "showTime": true, + "showUser": true + }, + "title": "Annotation list", + "type": "annolist" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 61 + }, + "id": 16, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.1.0-pre", + "title": "Stat", + "type": "stat" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 61 + }, + "id": 2, + "links": [], + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 4, + "spread": 120 + } + ], + "title": "Graph NG", + "type": "timeseries" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1 + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 69 + }, + "id": 14, + "options": { + "barWidth": 0.97, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "orientation": "auto", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "csvContent": "Series, Value\nBar 2, 20\nBar 3, 25\nBar 3, 55.4", + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Bar chart", + "type": "barchart" + }, + { + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 77 + }, + "id": 12, + "options": { + "showImage": true + }, + "title": "News panel", + "type": "news" + } + ], + "refresh": "", + "schemaVersion": 36, + "tags": ["gdev", "panel-tests", "all-panels"], + "templating": { + "list": [ + { + "current": {}, + "datasource": { + "type": "grafana-testdata-datasource" + }, + "definition": "*", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "query0", + "options": [], + "query": { + "query": "*", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": true, + "text": "7", + "value": "7" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "query1", + "options": [ + { + "selected": false, + "text": "1", + "value": "1" + }, + { + "selected": false, + "text": "5", + "value": "5" + }, + { + "selected": false, + "text": "6", + "value": "6" + }, + { "selected": true, "text": "7", "value": "7" - }, - "hide": 0, - "includeAll": false, - "multi": false, - "name": "query1", - "options": [ - { - "selected": false, - "text": "1", - "value": "1" - }, - { - "selected": false, - "text": "5", - "value": "5" - }, - { - "selected": false, - "text": "6", - "value": "6" - }, - { - "selected": true, - "text": "7", - "value": "7" - } - ], - "query": "1,5,6,7", - "queryValue": "", - "skipUrlSync": false, - "type": "custom" + } + ], + "query": "1,5,6,7", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + }, + { + "current": { + "selected": false, + "text": "", + "value": "" }, - { - "current": { - "selected": false, + "description": "This is some descriptive text", + "hide": 0, + "label": "Plain text", + "name": "text", + "options": [ + { + "selected": true, "text": "", "value": "" - }, - "description": "This is some descriptive text", - "hide": 0, - "label": "Plain text", - "name": "text", - "options": [ - { - "selected": true, - "text": "", - "value": "" - } - ], - "query": "", - "skipUrlSync": false, - "type": "textbox" - } - ] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Panel tests - All panels", - "uid": "n1jR8vnnz", - "version": 24 - } + } + ], + "query": "", + "skipUrlSync": false, + "type": "textbox" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Panel tests - All panels", + "uid": "n1jR8vnnz", + "version": 24 +} diff --git a/devenv/dev-dashboards/annotations/annotation-filtering.json b/devenv/dev-dashboards/annotations/annotation-filtering.json index a827d960d76..0a07c80ff0a 100644 --- a/devenv/dev-dashboards/annotations/annotation-filtering.json +++ b/devenv/dev-dashboards/annotations/annotation-filtering.json @@ -32,15 +32,12 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "enable": true, "filter": { "exclude": false, - "ids": [ - 1 - ] + "ids": [1] }, "iconColor": "red", "name": "Red, only panel 1", @@ -52,15 +49,12 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "enable": true, "filter": { "exclude": true, - "ids": [ - 1 - ] + "ids": [1] }, "iconColor": "yellow", "name": "Yellow - all except 1", @@ -72,16 +66,12 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "enable": true, "filter": { "exclude": false, - "ids": [ - 3, - 4 - ] + "ids": [3, 4] }, "iconColor": "dark-purple", "name": "Purple only panel 3+4", @@ -101,7 +91,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -176,7 +168,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -251,7 +245,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -326,7 +322,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { diff --git a/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_complex.json b/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_complex.json index 523a5b9db03..56187286696 100644 --- a/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_complex.json +++ b/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_complex.json @@ -596,13 +596,7 @@ "field": "value", "id": "1", "settings": { - "percents": [ - "25", - "50", - "75", - "95", - "99" - ] + "percents": ["25", "50", "75", "95", "99"] }, "type": "percentiles" } @@ -4565,13 +4559,7 @@ "field": "float", "id": "1", "settings": { - "percents": [ - "25", - "50", - "75", - "95", - "99" - ], + "percents": ["25", "50", "75", "95", "99"], "script": "_value * 2" }, "type": "percentiles" @@ -4695,9 +4683,7 @@ "field": "value", "id": "1", "settings": { - "percents": [ - "99" - ] + "percents": ["99"] }, "type": "percentiles" } @@ -4927,9 +4913,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -5015,9 +4999,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -5255,9 +5237,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -5291,7 +5271,7 @@ ], "title": "Logs", "type": "row" - }, + }, { "collapsed": true, "gridPos": { @@ -5739,11 +5719,7 @@ ], "refresh": "", "schemaVersion": 38, - "tags": [ - "gdev", - "elasticsearch", - "datasource-test" - ], + "tags": ["gdev", "elasticsearch", "datasource-test"], "templating": { "list": [ { diff --git a/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_migration.json b/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_migration.json index 47a36a310a9..1acd8d1246f 100644 --- a/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_migration.json +++ b/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_migration.json @@ -1051,13 +1051,7 @@ "field": "value", "id": "1", "settings": { - "percents": [ - "25", - "50", - "75", - "95", - "99" - ] + "percents": ["25", "50", "75", "95", "99"] }, "type": "percentiles" } @@ -1167,13 +1161,7 @@ "field": "value", "id": "1", "settings": { - "percents": [ - "25", - "50", - "75", - "95", - "99" - ] + "percents": ["25", "50", "75", "95", "99"] }, "type": "percentiles" } @@ -9165,13 +9153,7 @@ "field": "float", "id": "1", "settings": { - "percents": [ - "25", - "50", - "75", - "95", - "99" - ], + "percents": ["25", "50", "75", "95", "99"], "script": "_value * 2" }, "type": "percentiles" @@ -9295,13 +9277,7 @@ "field": "float", "id": "1", "settings": { - "percents": [ - "25", - "50", - "75", - "95", - "99" - ], + "percents": ["25", "50", "75", "95", "99"], "script": "_value * 2" }, "type": "percentiles" @@ -9436,9 +9412,7 @@ "field": "value", "id": "1", "settings": { - "percents": [ - "99" - ] + "percents": ["99"] }, "type": "percentiles" } @@ -9561,9 +9535,7 @@ "field": "value", "id": "1", "settings": { - "percents": [ - "99" - ] + "percents": ["99"] }, "type": "percentiles" } @@ -9959,9 +9931,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -10048,9 +10018,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -10290,9 +10258,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -10330,11 +10296,7 @@ ], "refresh": "", "schemaVersion": 38, - "tags": [ - "gdev", - "elasticsearch", - "datasource-test" - ], + "tags": ["gdev", "elasticsearch", "datasource-test"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_simple.json b/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_simple.json index b86f99334a8..bfa84378ba5 100644 --- a/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_simple.json +++ b/devenv/dev-dashboards/datasource-elasticsearch/elasticsearch_simple.json @@ -406,13 +406,7 @@ "id": "1", "meta": {}, "settings": { - "percents": [ - 25, - 50, - 75, - 95, - 99 - ] + "percents": [25, 50, 75, 95, 99] }, "type": "percentiles" } @@ -589,9 +583,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -979,9 +971,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -1059,9 +1049,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -1106,11 +1094,7 @@ ], "refresh": "", "schemaVersion": 38, - "tags": [ - "gdev", - "elasticsearch", - "datasource-test" - ], + "tags": ["gdev", "elasticsearch", "datasource-test"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/datasource-loki/loki_fakedata.json b/devenv/dev-dashboards/datasource-loki/loki_fakedata.json index 947c191ba61..57242a86953 100644 --- a/devenv/dev-dashboards/datasource-loki/loki_fakedata.json +++ b/devenv/dev-dashboards/datasource-loki/loki_fakedata.json @@ -276,9 +276,7 @@ "justifyMode": "auto", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -338,9 +336,7 @@ "justifyMode": "auto", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -366,11 +362,7 @@ ], "refresh": "", "schemaVersion": 37, - "tags": [ - "gdev", - "loki", - "datasource-test" - ], + "tags": ["gdev", "loki", "datasource-test"], "templating": { "list": [ { diff --git a/devenv/dev-dashboards/datasource-loki/loki_query_splitting.json b/devenv/dev-dashboards/datasource-loki/loki_query_splitting.json index 804b354ea9d..0cc5135a0f4 100644 --- a/devenv/dev-dashboards/datasource-loki/loki_query_splitting.json +++ b/devenv/dev-dashboards/datasource-loki/loki_query_splitting.json @@ -65,9 +65,7 @@ "alias": "id_count", "mode": "reduceRow", "reduce": { - "include": [ - "id" - ], + "include": ["id"], "reducer": "count" }, "replaceFields": false @@ -138,9 +136,7 @@ "alias": "id_count", "mode": "reduceRow", "reduce": { - "include": [ - "id" - ], + "include": ["id"], "reducer": "count" }, "replaceFields": false @@ -514,9 +510,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -607,9 +601,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -700,9 +692,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -816,9 +806,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -932,9 +920,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -1049,9 +1035,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -1136,4 +1120,4 @@ "uid": "dc4ec947-7e6b-4c3b-be8f-0abec7b0652a", "version": 2, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/datasource-testdata/demo1.json b/devenv/dev-dashboards/datasource-testdata/demo1.json index 56a57f50b9c..a2400c1716e 100644 --- a/devenv/dev-dashboards/datasource-testdata/demo1.json +++ b/devenv/dev-dashboards/datasource-testdata/demo1.json @@ -23,8 +23,7 @@ "panels": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -196,8 +195,7 @@ { "alias": "memory", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "A", @@ -207,8 +205,7 @@ { "alias": "cpu", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk", @@ -220,8 +217,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -349,8 +345,7 @@ { "alias": "logins", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -359,8 +354,7 @@ { "alias": "logins (-1h)", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk", @@ -373,8 +367,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -426,9 +419,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -440,8 +431,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "target": "scale(apps.backend.backend_01.counters.requests.count, 0.4)" @@ -452,8 +442,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -503,9 +492,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -517,8 +504,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "target": "apps.backend.backend_02.counters.requests.count" @@ -529,8 +515,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -582,9 +567,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -596,8 +579,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "target": "scale(apps.backend.backend_01.counters.requests.count, 0.8)" @@ -608,8 +590,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -659,9 +640,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -673,8 +652,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "target": "apps.backend.backend_04.counters.requests.count" @@ -685,8 +663,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -829,8 +806,7 @@ { "alias": "web_server_01", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -839,8 +815,7 @@ { "alias": "web_server_02", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk" @@ -848,8 +823,7 @@ { "alias": "web_server_03", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "random_walk" @@ -857,8 +831,7 @@ { "alias": "web_server_04", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "random_walk" @@ -869,8 +842,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -922,9 +894,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -936,8 +906,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "target": "scale(apps.backend.backend_01.counters.requests.count, 0.2)" @@ -948,8 +917,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -999,9 +967,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1013,8 +979,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "target": "scale(apps.backend.backend_01.counters.requests.count, 0.7)" @@ -1025,8 +990,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1076,9 +1040,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1090,8 +1052,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "target": "scale(apps.backend.backend_03.counters.requests.count, 0.3)" @@ -1102,8 +1063,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1155,9 +1115,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1169,8 +1127,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "target": "scale(apps.backend.backend_01.counters.requests.count, 0.2)" @@ -1181,8 +1138,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1309,8 +1265,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -1325,10 +1280,7 @@ "preload": false, "refresh": false, "schemaVersion": 41, - "tags": [ - "gdev", - "demo" - ], + "tags": ["gdev", "demo"], "templating": { "list": [] }, @@ -1341,4 +1293,4 @@ "title": "TestData - Demo Dashboard", "uid": "000000003", "version": 2 -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/datasource-testdata/new_features_in_v74.json b/devenv/dev-dashboards/datasource-testdata/new_features_in_v74.json index ae996d5a3af..3f495d65f8c 100644 --- a/devenv/dev-dashboards/datasource-testdata/new_features_in_v74.json +++ b/devenv/dev-dashboards/datasource-testdata/new_features_in_v74.json @@ -64,7 +64,9 @@ "type": "row" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -177,7 +179,9 @@ "type": "timeseries" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -290,7 +294,9 @@ "type": "timeseries" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -404,7 +410,9 @@ }, { "cacheTimeout": null, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": {}, @@ -456,7 +464,9 @@ "type": "row" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -555,7 +565,9 @@ "type": "timeseries" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -656,7 +668,9 @@ "type": "timeseries" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "", "fieldConfig": { "defaults": { @@ -758,7 +772,9 @@ "type": "timeseries" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "", "fieldConfig": { "defaults": { @@ -861,7 +877,9 @@ }, { "cacheTimeout": null, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": {}, @@ -913,7 +931,9 @@ "type": "row" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1018,9 +1038,7 @@ "maxDataPoints": 50, "options": { "legend": { - "calcs": [ - "last" - ], + "calcs": ["last"], "displayMode": "list", "placement": "bottom" }, @@ -1121,7 +1139,9 @@ }, { "cacheTimeout": null, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": {}, @@ -1173,7 +1193,9 @@ "type": "row" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1202,10 +1224,7 @@ }, "lineInterpolation": "linear", "lineStyle": { - "dash": [ - 10, - 10 - ], + "dash": [10, 10], "fill": "dash" }, "lineWidth": 2, @@ -1242,10 +1261,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 10, - 20 - ], + "dash": [10, 20], "fill": "dash" } } @@ -1260,10 +1276,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 10, - 30 - ], + "dash": [10, 30], "fill": "dash" } } @@ -1278,11 +1291,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 30, - 3, - 3 - ], + "dash": [30, 3, 3], "fill": "dash" } } @@ -1308,10 +1317,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 0, - 10 - ], + "dash": [0, 10], "fill": "dot" } } @@ -1499,7 +1505,9 @@ "type": "timeseries" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1525,10 +1533,7 @@ }, "lineInterpolation": "linear", "lineStyle": { - "dash": [ - 10, - 10 - ], + "dash": [10, 10], "fill": "solid" }, "lineWidth": 1, @@ -1625,7 +1630,9 @@ "type": "timeseries" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1651,10 +1658,7 @@ }, "lineInterpolation": "linear", "lineStyle": { - "dash": [ - 10, - 10 - ], + "dash": [10, 10], "fill": "solid" }, "lineWidth": 1, @@ -1752,7 +1756,9 @@ }, { "cacheTimeout": null, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": {}, @@ -1790,7 +1796,9 @@ "type": "text" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1929,7 +1937,9 @@ }, { "cacheTimeout": null, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": {}, @@ -1981,7 +1991,9 @@ "type": "row" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2079,9 +2091,7 @@ "maxDataPoints": 500, "options": { "legend": { - "calcs": [ - "last" - ], + "calcs": ["last"], "displayMode": "list", "placement": "bottom" }, @@ -2125,7 +2135,9 @@ }, { "cacheTimeout": null, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": {}, @@ -2165,11 +2177,7 @@ ], "refresh": false, "schemaVersion": 27, - "tags": [ - "gdev", - "graph-ng", - "demo" - ], + "tags": ["gdev", "graph-ng", "demo"], "templating": { "list": [] }, @@ -2178,17 +2186,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "America/New_York", "title": "New Features in v7.4", diff --git a/devenv/dev-dashboards/datasource-testdata/new_features_in_v8.json b/devenv/dev-dashboards/datasource-testdata/new_features_in_v8.json index 75c322564af..797ec076556 100644 --- a/devenv/dev-dashboards/datasource-testdata/new_features_in_v8.json +++ b/devenv/dev-dashboards/datasource-testdata/new_features_in_v8.json @@ -41,7 +41,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "gridPos": { "h": 8, @@ -68,7 +68,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -165,7 +165,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "gridPos": { "h": 15, @@ -345,7 +345,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "gridPos": { "h": 13, @@ -372,7 +372,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -437,7 +437,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -527,7 +527,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "gridPos": { "h": 3, @@ -554,7 +554,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -674,7 +674,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -767,7 +767,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "gridPos": { "h": 3, @@ -794,7 +794,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "gridPos": { "h": 6, @@ -821,7 +821,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -942,7 +942,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1063,7 +1063,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1184,7 +1184,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "gridPos": { "h": 8, @@ -1211,7 +1211,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1315,7 +1315,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -1422,7 +1422,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -1529,7 +1529,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "gridPos": { "h": 5, @@ -1556,7 +1556,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1669,9 +1669,7 @@ "maxDataPoints": 50, "options": { "legend": { - "calcs": [ - "last" - ], + "calcs": ["last"], "displayMode": "list", "placement": "bottom" }, @@ -1773,7 +1771,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "gridPos": { "h": 5, @@ -1800,7 +1798,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1821,10 +1819,7 @@ }, "lineInterpolation": "linear", "lineStyle": { - "dash": [ - 10, - 10 - ], + "dash": [10, 10], "fill": "dash" }, "lineWidth": 2, @@ -1866,10 +1861,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 10, - 20 - ], + "dash": [10, 20], "fill": "dash" } } @@ -1884,10 +1876,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 10, - 30 - ], + "dash": [10, 30], "fill": "dash" } } @@ -1902,11 +1891,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 30, - 3, - 3 - ], + "dash": [30, 3, 3], "fill": "dash" } } @@ -1932,10 +1917,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 0, - 10 - ], + "dash": [0, 10], "fill": "dot" } } @@ -2125,7 +2107,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2146,10 +2128,7 @@ }, "lineInterpolation": "linear", "lineStyle": { - "dash": [ - 10, - 10 - ], + "dash": [10, 10], "fill": "dash" }, "lineWidth": 2, @@ -2191,10 +2170,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 10, - 20 - ], + "dash": [10, 20], "fill": "dash" } } @@ -2209,10 +2185,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 10, - 30 - ], + "dash": [10, 30], "fill": "dash" } } @@ -2227,11 +2200,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 30, - 3, - 3 - ], + "dash": [30, 3, 3], "fill": "dash" } } @@ -2257,10 +2226,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 0, - 10 - ], + "dash": [0, 10], "fill": "dot" } } @@ -2450,7 +2416,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2471,10 +2437,7 @@ }, "lineInterpolation": "linear", "lineStyle": { - "dash": [ - 10, - 10 - ], + "dash": [10, 10], "fill": "solid" }, "lineWidth": 1, @@ -2578,7 +2541,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "gridPos": { "h": 3, @@ -2605,7 +2568,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2710,7 +2673,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2814,11 +2777,7 @@ ], "refresh": false, "schemaVersion": 33, - "tags": [ - "gdev", - "graph-ng", - "demo" - ], + "tags": ["gdev", "graph-ng", "demo"], "templating": { "list": [] }, @@ -2827,17 +2786,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "New Features in v8.0", diff --git a/devenv/dev-dashboards/e2e-repeats/Repeating-Kitchen-Sink.json b/devenv/dev-dashboards/e2e-repeats/Repeating-Kitchen-Sink.json index bdb3ab46b5c..82f271830fb 100644 --- a/devenv/dev-dashboards/e2e-repeats/Repeating-Kitchen-Sink.json +++ b/devenv/dev-dashboards/e2e-repeats/Repeating-Kitchen-Sink.json @@ -27,7 +27,7 @@ "panels": [ { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -115,7 +115,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -192,7 +192,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -267,7 +267,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -350,12 +350,8 @@ { "current": { "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, @@ -391,12 +387,8 @@ { "current": { "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, @@ -432,12 +424,8 @@ { "current": { "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, diff --git a/devenv/dev-dashboards/e2e-repeats/Repeating-a-panel-horizontally.json b/devenv/dev-dashboards/e2e-repeats/Repeating-a-panel-horizontally.json index 2def57915d9..d90b2f02fdb 100644 --- a/devenv/dev-dashboards/e2e-repeats/Repeating-a-panel-horizontally.json +++ b/devenv/dev-dashboards/e2e-repeats/Repeating-a-panel-horizontally.json @@ -30,7 +30,7 @@ "panels": [ { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -112,12 +112,8 @@ { "current": { "selected": false, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, @@ -153,12 +149,8 @@ { "current": { "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, diff --git a/devenv/dev-dashboards/e2e-repeats/Repeating-a-panel-vertically.json b/devenv/dev-dashboards/e2e-repeats/Repeating-a-panel-vertically.json index 9a450a79b4e..f64a8b06ed0 100644 --- a/devenv/dev-dashboards/e2e-repeats/Repeating-a-panel-vertically.json +++ b/devenv/dev-dashboards/e2e-repeats/Repeating-a-panel-vertically.json @@ -26,7 +26,7 @@ "panels": [ { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -108,12 +108,8 @@ { "current": { "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, @@ -149,12 +145,8 @@ { "current": { "selected": true, - "text": [ - "1" - ], - "value": [ - "1" - ] + "text": ["1"], + "value": ["1"] }, "hide": 0, "includeAll": true, diff --git a/devenv/dev-dashboards/e2e-repeats/Repeating-a-row-with-a-repeating-horizontal-panel.json b/devenv/dev-dashboards/e2e-repeats/Repeating-a-row-with-a-repeating-horizontal-panel.json index 5278ec5f319..f0b1ad3dedf 100644 --- a/devenv/dev-dashboards/e2e-repeats/Repeating-a-row-with-a-repeating-horizontal-panel.json +++ b/devenv/dev-dashboards/e2e-repeats/Repeating-a-row-with-a-repeating-horizontal-panel.json @@ -41,7 +41,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -124,12 +124,8 @@ { "current": { "selected": false, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, @@ -165,12 +161,8 @@ { "current": { "selected": false, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, @@ -206,12 +198,8 @@ { "current": { "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, diff --git a/devenv/dev-dashboards/e2e-repeats/Repeating-a-row-with-a-repeating-vertical-panel.json b/devenv/dev-dashboards/e2e-repeats/Repeating-a-row-with-a-repeating-vertical-panel.json index 34cfd58c81d..b19b4b8d2c9 100644 --- a/devenv/dev-dashboards/e2e-repeats/Repeating-a-row-with-a-repeating-vertical-panel.json +++ b/devenv/dev-dashboards/e2e-repeats/Repeating-a-row-with-a-repeating-vertical-panel.json @@ -41,7 +41,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -123,12 +123,8 @@ { "current": { "selected": false, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, @@ -164,12 +160,8 @@ { "current": { "selected": false, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, @@ -205,12 +197,8 @@ { "current": { "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, diff --git a/devenv/dev-dashboards/e2e-repeats/Repeating-an-empty-row.json b/devenv/dev-dashboards/e2e-repeats/Repeating-an-empty-row.json index 9220c5d1d7b..8ac2547df4b 100644 --- a/devenv/dev-dashboards/e2e-repeats/Repeating-an-empty-row.json +++ b/devenv/dev-dashboards/e2e-repeats/Repeating-an-empty-row.json @@ -47,12 +47,8 @@ { "current": { "selected": false, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, @@ -88,12 +84,8 @@ { "current": { "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, @@ -129,12 +121,8 @@ { "current": { "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, diff --git a/devenv/dev-dashboards/extensions/link-onclick-extensions.json b/devenv/dev-dashboards/extensions/link-onclick-extensions.json index 5119eb4c1b9..b7217bdca36 100644 --- a/devenv/dev-dashboards/extensions/link-onclick-extensions.json +++ b/devenv/dev-dashboards/extensions/link-onclick-extensions.json @@ -23,7 +23,7 @@ "panels": [ { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -101,7 +101,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -144,9 +144,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true, @@ -158,7 +156,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -191,9 +189,7 @@ }, "pieType": "pie", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -205,7 +201,7 @@ "targets": [ { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -213,7 +209,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "B", @@ -226,7 +222,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -302,7 +298,7 @@ "targets": [ { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -310,7 +306,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "B", @@ -339,4 +335,4 @@ "uid": "dbfb47c5-e5e5-4d28-8ac7-35f349b95946", "version": 1, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/extensions/link-path-extensions.json b/devenv/dev-dashboards/extensions/link-path-extensions.json index 6ef0669e371..821a3b00bdb 100644 --- a/devenv/dev-dashboards/extensions/link-path-extensions.json +++ b/devenv/dev-dashboards/extensions/link-path-extensions.json @@ -1,237 +1,233 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 1, - "links": [], - "liveNow": false, - "panels": [ + "annotations": { + "list": [ { + "builtIn": 1, "datasource": { - "type": "testdata" + "type": "grafana", + "uid": "-- Grafana --" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 0 - }, - "id": 6, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "showRowNums": false - }, - "pluginVersion": "9.5.0-53420pre", - "title": "No extensions", - "type": "table" - }, - { - "datasource": { - "type": "testdata" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - } - }, - "mappings": [] - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 0 - }, - "id": 4, - "options": { - "legend": { - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "pieType": "pie", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "testdata" - }, - "refId": "A", - "scenarioId": "random_walk", - "seriesCount": 4 - } - ], - "title": "Link with new name", - "type": "piechart" - }, - { - "datasource": { - "type": "testdata" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 0, - "y": 8 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "title": "Link with defaults", - "type": "timeseries" + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" } - ], - "refresh": "", - "schemaVersion": 38, - "style": "dark", - "tags": [], - "templating": { - "list": [] + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 6, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "showRowNums": false + }, + "pluginVersion": "9.5.0-53420pre", + "title": "No extensions", + "type": "table" }, - "time": { - "from": "now-6h", - "to": "now" + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "pieType": "pie", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 4 + } + ], + "title": "Link with new name", + "type": "piechart" }, - "timepicker": {}, - "timezone": "", - "title": "Link Extensions (path)", - "uid": "d1fbb077-cd44-4738-8c8a-d4e66748b719", - "version": 3, - "weekStart": "" - } \ No newline at end of file + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 8 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "title": "Link with defaults", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Link Extensions (path)", + "uid": "d1fbb077-cd44-4738-8c8a-d4e66748b719", + "version": 3, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/feature-templating/datadata-macros.json b/devenv/dev-dashboards/feature-templating/datadata-macros.json index a0068fea71e..815ad3f7259 100644 --- a/devenv/dev-dashboards/feature-templating/datadata-macros.json +++ b/devenv/dev-dashboards/feature-templating/datadata-macros.json @@ -1,870 +1,832 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 1267, - "links": [], - "liveNow": false, - "panels": [ + "annotations": { + "list": [ { + "builtIn": 1, "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana", + "uid": "-- Grafana --" }, - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 8, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "* `__all_variables`=${__all_variables}\n* `__url_time_range`=${__url_time_range}", - "mode": "markdown" - }, - "pluginVersion": "9.5.0-pre", - "title": "Panel Title", - "type": "text" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "links": [ - { - "targetBlank": true, - "title": "value=${__value.raw}&time=${__value.time}&__value:percentencode=${__value:percentencode}&text=${__value.text}", - "url": "value=${__value.raw}&time=${__value.time}justvalue=${__value:percentencode}&text=${__value.text}" - } - ], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 3 - }, - "id": 2, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "showRowNums": false - }, - "pluginVersion": "9.5.0-pre", - "title": "DataLink: with __value.raw=&__value.time=&__value:percentencode=", - "type": "table" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "links": [ - { - "targetBlank": true, - "title": "Value link", - "url": "value=${__value.raw}" - } - ], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 3 - }, - "id": 3, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.5.0-pre", - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "random_walk", - "seriesCount": 5 - } - ], - "title": "Stat panel with __value.raw ", - "type": "stat" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "continuous-GrYlRd" - }, - "links": [ - { - "title": "${__value.raw}", - "url": "${__value.raw}" - } - ], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 10 - }, - "id": 6, - "options": { - "displayMode": "basic", - "minVizHeight": 10, - "minVizWidth": 0, - "orientation": "horizontal", - "reduceOptions": { - "calcs": [], - "fields": "", - "values": true - }, - "showUnfilled": true, - "valueMode": "color" - }, - "pluginVersion": "9.5.0-pre", - "targets": [ - { - "csvFileName": "browser_marketshare.csv", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_file" - } - ], - "title": "data link __value.raw", - "transformations": [ - { - "id": "limit", - "options": { - "limitField": 5 - } - } - ], - "type": "bargauge" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "description": "Since this is using getFrameDisplayName it works kind badly (especially with testdata) and only returns the `Series (refId)`. \n\nSo this should show:\n* Series (Query1)\n* Series (Query2)", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "displayName": "${__series.name}", - "links": [ - { - "targetBlank": true, - "title": "Value link", - "url": "value=${__calc}" - } - ], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 10 - }, - "id": 12, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "textMode": "auto" - }, - "pluginVersion": "9.5.0-pre", - "targets": [ - { - "alias": "", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "Query1", - "scenarioId": "random_walk", - "seriesCount": 1 - }, - { - "alias": "", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "hide": false, - "refId": "Query2", - "scenarioId": "random_walk", - "seriesCount": 1 - } - ], - "title": "${series.name} in display name", - "type": "stat" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "links": [ - { - "title": "__data.refId=${__data.refId}&__data.fields[0]=${__data.fields[0]}&cluster=${__field.labels.cluster}", - "url": "refId=${__data.refId}&__data.fields[0]=${__data.fields[0]}&cluster=${__field.labels.cluster}" - } - ], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 17 - }, - "id": 11, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "showRowNums": false - }, - "pluginVersion": "9.5.0-pre", - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "labels": "cluster=US", - "refId": "A", - "scenarioId": "random_walk" - } - ], - "title": "DataLink: refId=${__data.refId}&__data.fields[0]=${__data.fields[0]}&cluster=${__field.labels.cluster}", - "type": "table" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "links": [ - { - "title": "${__value.raw}", - "url": "${__value.raw}" - } - ], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 17 - }, - "id": 10, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "alias": "10,20,30,40", - "csvContent": "Time, value, test\n\"2023-03-24T17:12:12.347Z\", 10,hello\n\"2023-03-24T17:22:12.347Z\", 20,asd\n\"2023-03-24T17:32:12.347Z\", 30,asd2\n\"2023-03-24T17:42:12.347Z\", 40,as34\n", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_content" - }, - { - "alias": "5,6,7", - "csvContent": "Time, value, test\n\"2023-03-24T17:12:12.347Z\", 5,hello\n\"2023-03-24T17:22:12.347Z\", 6,asd\n\"2023-03-24T17:42:12.347Z\", 7,as34\n", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "B", - "scenarioId": "csv_content" - } - ], - "title": "Data links with ${__value.raw}", - "transformations": [ - { - "id": "joinByField", - "options": {} - } - ], - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "links": [ - { - "title": "__field.name=${__field.name}&__field.labels.cluster=${__field.labels.cluster}", - "url": "__field.name=${__field.name}&__field.labels.cluster=${__field.labels.cluster}" - } - ], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 24 - }, - "id": 13, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "showRowNums": false - }, - "pluginVersion": "9.5.0-pre", - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "labels": "cluster=US", - "refId": "A", - "scenarioId": "random_walk" - } - ], - "title": "DataLink: __field.name=&__field.labels.cluster", - "type": "table" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "description": "The stat display names should be \n* Stockholm = Bad\n* New York = Good \n", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "displayName": "$__cell_0 = $__cell_2", - "links": [], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 24 - }, - "id": 14, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": true - }, - "textMode": "auto" - }, - "pluginVersion": "9.5.0-pre", - "targets": [ - { - "alias": "", - "csvContent": "name, value, name2\nStockholm, 10, Good\nNew York, 15, Bad", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "DisplayName with __cell_0 = __cell_2", - "type": "stat" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "description": "The stat display names should be \n* Stockholm = Bad\n* New York = Good \n", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "displayName": "${__field.name}", - "links": [ - { - "targetBlank": true, - "title": "Value link", - "url": "value=${__calc}" - } - ], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 31 - }, - "id": 15, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "showRowNums": false - }, - "pluginVersion": "9.5.0-pre", - "targets": [ - { - "alias": "", - "csvContent": "name, value, name2\nStockholm, 10, Good\nNew York, 15, Bad", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "DisplayName: __field.name", - "type": "table" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "description": "The stat display names should be \n* Stockholm = Bad\n* New York = Good \n", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "displayName": "${__data.fields[0]} = ${__data.fields[2]}", - "links": [ - { - "targetBlank": true, - "title": "__data.fields[0] = ${__data.fields[0]} = __value.raw = ${__value.raw}", - "url": "__data.fields[0] = ${__data.fields[0]} = __value.raw = ${__value.raw}" - } - ], - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 31 - }, - "id": 16, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": true - }, - "textMode": "auto" - }, - "pluginVersion": "9.5.0-pre", - "targets": [ - { - "alias": "", - "csvContent": "name, value, name2\nStockholm, 10, Good\nNew York, 15, Bad", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "$__data.fields[0] = $__data.fields[2] with datalinks", - "type": "stat" + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" } - ], - "refresh": "", - "schemaVersion": 38, - "tags": ["gdev", "templating"], - "templating": { - "list": [ + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1267, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "gridPos": { + "h": 3, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 8, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "* `__all_variables`=${__all_variables}\n* `__url_time_range`=${__url_time_range}", + "mode": "markdown" + }, + "pluginVersion": "9.5.0-pre", + "title": "Panel Title", + "type": "text" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "links": [ + { + "targetBlank": true, + "title": "value=${__value.raw}&time=${__value.time}&__value:percentencode=${__value:percentencode}&text=${__value.text}", + "url": "value=${__value.raw}&time=${__value.time}justvalue=${__value:percentencode}&text=${__value.text}" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 3 + }, + "id": 2, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "showRowNums": false + }, + "pluginVersion": "9.5.0-pre", + "title": "DataLink: with __value.raw=&__value.time=&__value:percentencode=", + "type": "table" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "links": [ + { + "targetBlank": true, + "title": "Value link", + "url": "value=${__value.raw}" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 3 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.0-pre", + "targets": [ { - "current": { - "selected": false, + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 5 + } + ], + "title": "Stat panel with __value.raw ", + "type": "stat" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "links": [ + { + "title": "${__value.raw}", + "url": "${__value.raw}" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 6, + "options": { + "displayMode": "basic", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [], + "fields": "", + "values": true + }, + "showUnfilled": true, + "valueMode": "color" + }, + "pluginVersion": "9.5.0-pre", + "targets": [ + { + "csvFileName": "browser_marketshare.csv", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_file" + } + ], + "title": "data link __value.raw", + "transformations": [ + { + "id": "limit", + "options": { + "limitField": 5 + } + } + ], + "type": "bargauge" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "description": "Since this is using getFrameDisplayName it works kind badly (especially with testdata) and only returns the `Series (refId)`. \n\nSo this should show:\n* Series (Query1)\n* Series (Query2)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "${__series.name}", + "links": [ + { + "targetBlank": true, + "title": "Value link", + "url": "value=${__calc}" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 12, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.0-pre", + "targets": [ + { + "alias": "", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "Query1", + "scenarioId": "random_walk", + "seriesCount": 1 + }, + { + "alias": "", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "hide": false, + "refId": "Query2", + "scenarioId": "random_walk", + "seriesCount": 1 + } + ], + "title": "${series.name} in display name", + "type": "stat" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "links": [ + { + "title": "__data.refId=${__data.refId}&__data.fields[0]=${__data.fields[0]}&cluster=${__field.labels.cluster}", + "url": "refId=${__data.refId}&__data.fields[0]=${__data.fields[0]}&cluster=${__field.labels.cluster}" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 11, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "showRowNums": false + }, + "pluginVersion": "9.5.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "labels": "cluster=US", + "refId": "A", + "scenarioId": "random_walk" + } + ], + "title": "DataLink: refId=${__data.refId}&__data.fields[0]=${__data.fields[0]}&cluster=${__field.labels.cluster}", + "type": "table" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "links": [ + { + "title": "${__value.raw}", + "url": "${__value.raw}" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "alias": "10,20,30,40", + "csvContent": "Time, value, test\n\"2023-03-24T17:12:12.347Z\", 10,hello\n\"2023-03-24T17:22:12.347Z\", 20,asd\n\"2023-03-24T17:32:12.347Z\", 30,asd2\n\"2023-03-24T17:42:12.347Z\", 40,as34\n", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_content" + }, + { + "alias": "5,6,7", + "csvContent": "Time, value, test\n\"2023-03-24T17:12:12.347Z\", 5,hello\n\"2023-03-24T17:22:12.347Z\", 6,asd\n\"2023-03-24T17:42:12.347Z\", 7,as34\n", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "B", + "scenarioId": "csv_content" + } + ], + "title": "Data links with ${__value.raw}", + "transformations": [ + { + "id": "joinByField", + "options": {} + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "links": [ + { + "title": "__field.name=${__field.name}&__field.labels.cluster=${__field.labels.cluster}", + "url": "__field.name=${__field.name}&__field.labels.cluster=${__field.labels.cluster}" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 13, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "showRowNums": false + }, + "pluginVersion": "9.5.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "labels": "cluster=US", + "refId": "A", + "scenarioId": "random_walk" + } + ], + "title": "DataLink: __field.name=&__field.labels.cluster", + "type": "table" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "description": "The stat display names should be \n* Stockholm = Bad\n* New York = Good \n", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "$__cell_0 = $__cell_2", + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 14, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.0-pre", + "targets": [ + { + "alias": "", + "csvContent": "name, value, name2\nStockholm, 10, Good\nNew York, 15, Bad", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "DisplayName with __cell_0 = __cell_2", + "type": "stat" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "description": "The stat display names should be \n* Stockholm = Bad\n* New York = Good \n", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "displayName": "${__field.name}", + "links": [ + { + "targetBlank": true, + "title": "Value link", + "url": "value=${__calc}" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 31 + }, + "id": 15, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "showRowNums": false + }, + "pluginVersion": "9.5.0-pre", + "targets": [ + { + "alias": "", + "csvContent": "name, value, name2\nStockholm, 10, Good\nNew York, 15, Bad", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "DisplayName: __field.name", + "type": "table" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "description": "The stat display names should be \n* Stockholm = Bad\n* New York = Good \n", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "displayName": "${__data.fields[0]} = ${__data.fields[2]}", + "links": [ + { + "targetBlank": true, + "title": "__data.fields[0] = ${__data.fields[0]} = __value.raw = ${__value.raw}", + "url": "__data.fields[0] = ${__data.fields[0]} = __value.raw = ${__value.raw}" + } + ], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 31 + }, + "id": 16, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "textMode": "auto" + }, + "pluginVersion": "9.5.0-pre", + "targets": [ + { + "alias": "", + "csvContent": "name, value, name2\nStockholm, 10, Good\nNew York, 15, Bad", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "$__data.fields[0] = $__data.fields[2] with datalinks", + "type": "stat" + } + ], + "refresh": "", + "schemaVersion": 38, + "tags": ["gdev", "templating"], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "A", + "value": "A" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "customVar", + "options": [ + { + "selected": true, "text": "A", "value": "A" }, - "hide": 0, - "includeAll": false, - "multi": false, - "name": "customVar", - "options": [ - { - "selected": true, - "text": "A", - "value": "A" - }, - { - "selected": false, - "text": "B", - "value": "B" - }, - { - "selected": false, - "text": "C", - "value": "C" - } - ], - "query": "A,B,C", - "queryValue": "", - "skipUrlSync": false, - "type": "custom" - } - ] - }, - "time": { - "from": "2023-03-24T17:12:12.347Z", - "to": "2023-03-24T17:42:12.347Z" - }, - "timepicker": {}, - "timezone": "", - "title": "Templating - Macros", - "uid": "e7c29343-6d1e-4167-9c13-803fe5be8c46", - "version": 48, - "weekStart": "" - } + { + "selected": false, + "text": "B", + "value": "B" + }, + { + "selected": false, + "text": "C", + "value": "C" + } + ], + "query": "A,B,C", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + } + ] + }, + "time": { + "from": "2023-03-24T17:12:12.347Z", + "to": "2023-03-24T17:42:12.347Z" + }, + "timepicker": {}, + "timezone": "", + "title": "Templating - Macros", + "uid": "e7c29343-6d1e-4167-9c13-803fe5be8c46", + "version": 48, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/feature-templating/templating-repeating-panels.json b/devenv/dev-dashboards/feature-templating/templating-repeating-panels.json index ee69cd7e110..ebf8b77d814 100644 --- a/devenv/dev-dashboards/feature-templating/templating-repeating-panels.json +++ b/devenv/dev-dashboards/feature-templating/templating-repeating-panels.json @@ -42,7 +42,9 @@ "type": "text" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -123,8 +125,7 @@ { "alias": "server = $server", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -156,7 +157,9 @@ "type": "text" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -239,8 +242,7 @@ { "alias": "host = ${host:text} / id = $host", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -259,16 +261,8 @@ { "current": { "selected": false, - "text": [ - "A", - "B", - "C" - ], - "value": [ - "A", - "B", - "C" - ] + "text": ["A", "B", "C"], + "value": ["A", "B", "C"] }, "hide": 0, "includeAll": true, @@ -354,12 +348,8 @@ { "current": { "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "hide": 0, "includeAll": true, @@ -414,4 +404,4 @@ "uid": "templating-repeating-panels", "version": 37, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/feature-templating/templating-repeating-rows.json b/devenv/dev-dashboards/feature-templating/templating-repeating-rows.json index af630afd1f5..1c407e4a0e5 100644 --- a/devenv/dev-dashboards/feature-templating/templating-repeating-rows.json +++ b/devenv/dev-dashboards/feature-templating/templating-repeating-rows.json @@ -69,7 +69,9 @@ "type": "row" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -150,8 +152,7 @@ { "alias": "server = $server, pod id = $pod ", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -198,23 +199,14 @@ ], "refresh": "", "schemaVersion": 38, - "tags": [ - "templating", - "gdev" - ], + "tags": ["templating", "gdev"], "templating": { "list": [ { "current": { "selected": true, - "text": [ - "A", - "B" - ], - "value": [ - "A", - "B" - ] + "text": ["A", "B"], + "value": ["A", "B"] }, "hide": 0, "includeAll": true, @@ -300,14 +292,8 @@ { "current": { "selected": true, - "text": [ - "Bob", - "Rob" - ], - "value": [ - "1", - "2" - ] + "text": ["Bob", "Rob"], + "value": ["1", "2"] }, "hide": 0, "includeAll": true, @@ -362,4 +348,4 @@ "uid": "Repeating-rows-uid", "version": 1, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/feature-templating/testdata-nested-variables-drilldown.json b/devenv/dev-dashboards/feature-templating/testdata-nested-variables-drilldown.json index 73b9a970880..357a764f2c6 100644 --- a/devenv/dev-dashboards/feature-templating/testdata-nested-variables-drilldown.json +++ b/devenv/dev-dashboards/feature-templating/testdata-nested-variables-drilldown.json @@ -128,7 +128,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 0, "gridPos": { @@ -221,7 +223,9 @@ "text": "A", "value": ["A"] }, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "definition": "*", "hide": 0, "includeAll": true, @@ -246,7 +250,9 @@ "text": "AA", "value": ["AA"] }, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "definition": "$datacenter.*", "hide": 0, "includeAll": true, @@ -271,7 +277,9 @@ "text": "All", "value": ["$__all"] }, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "definition": "$datacenter.$server.*", "hide": 0, "includeAll": true, diff --git a/devenv/dev-dashboards/feature-templating/testdata-nested-variables.json b/devenv/dev-dashboards/feature-templating/testdata-nested-variables.json index 6c3b9760e66..ede752ed948 100644 --- a/devenv/dev-dashboards/feature-templating/testdata-nested-variables.json +++ b/devenv/dev-dashboards/feature-templating/testdata-nested-variables.json @@ -51,7 +51,9 @@ "colorBackground": false, "colorValue": false, "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "custom": {} @@ -345,7 +347,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "custom": {} @@ -446,7 +450,9 @@ "text": "A", "value": ["A"] }, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "definition": "*", "hide": 0, "includeAll": true, @@ -472,7 +478,9 @@ "text": "AA", "value": ["AA"] }, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "definition": "$datacenter.*", "hide": 0, "includeAll": true, @@ -498,7 +506,9 @@ "text": "All", "value": ["$__all"] }, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "definition": "$datacenter.$server.*", "hide": 0, "includeAll": true, diff --git a/devenv/dev-dashboards/feature-templating/testdata-variables-that-update-on-time-change.json b/devenv/dev-dashboards/feature-templating/testdata-variables-that-update-on-time-change.json index b7d1b02a9ab..48d6dce2277 100644 --- a/devenv/dev-dashboards/feature-templating/testdata-variables-that-update-on-time-change.json +++ b/devenv/dev-dashboards/feature-templating/testdata-variables-that-update-on-time-change.json @@ -123,7 +123,9 @@ "text": "value.1584434137814", "value": "value.1584434137814" }, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "definition": "value.$__from", "hide": 0, "includeAll": false, @@ -149,7 +151,9 @@ "text": "value.value.1584434072074", "value": "value.value.1584434072074" }, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "definition": "value.$updatesOnTime", "hide": 0, "includeAll": false, diff --git a/devenv/dev-dashboards/home.json b/devenv/dev-dashboards/home.json index 410e1bcf290..5f87229e532 100644 --- a/devenv/dev-dashboards/home.json +++ b/devenv/dev-dashboards/home.json @@ -29,7 +29,7 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "gridPos": { "h": 26, "w": 6, @@ -53,7 +53,7 @@ "type": "dashlist" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "gridPos": { "h": 13, "w": 6, @@ -69,19 +69,15 @@ "showRecentlyViewed": false, "showSearch": true, "showStarred": false, - "tags": [ - "panel-tests" - ] + "tags": ["panel-tests"] }, "pluginVersion": "9.0.0-pre", - "tags": [ - "panel-tests" - ], + "tags": ["panel-tests"], "title": "tag: panel-tests", "type": "dashlist" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "gridPos": { "h": 13, "w": 6, @@ -97,21 +93,15 @@ "showRecentlyViewed": false, "showSearch": true, "showStarred": false, - "tags": [ - "gdev", - "demo" - ] + "tags": ["gdev", "demo"] }, "pluginVersion": "9.0.0-pre", - "tags": [ - "gdev", - "demo" - ], + "tags": ["gdev", "demo"], "title": "tag: dashboard-demo", "type": "dashlist" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "gridPos": { "h": 26, "w": 6, @@ -127,21 +117,15 @@ "showRecentlyViewed": false, "showSearch": true, "showStarred": false, - "tags": [ - "gdev", - "datasource-test" - ] + "tags": ["gdev", "datasource-test"] }, "pluginVersion": "9.0.0-pre", - "tags": [ - "gdev", - "datasource-test" - ], + "tags": ["gdev", "datasource-test"], "title": "Data source tests", "type": "dashlist" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "gridPos": { "h": 13, "w": 6, @@ -157,21 +141,15 @@ "showRecentlyViewed": false, "showSearch": true, "showStarred": false, - "tags": [ - "templating", - "gdev" - ] + "tags": ["templating", "gdev"] }, "pluginVersion": "9.0.0-pre", - "tags": [ - "templating", - "gdev" - ], + "tags": ["templating", "gdev"], "title": "tag: templating ", "type": "dashlist" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "gridPos": { "h": 13, "w": 6, @@ -187,16 +165,10 @@ "showRecentlyViewed": false, "showSearch": true, "showStarred": false, - "tags": [ - "gdev", - "transform" - ] + "tags": ["gdev", "transform"] }, "pluginVersion": "9.0.0-pre", - "tags": [ - "gdev", - "demo" - ], + "tags": ["gdev", "demo"], "title": "tag: transforms", "type": "dashlist" } @@ -211,18 +183,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Grafana Dev Overview & Home", diff --git a/devenv/dev-dashboards/live/live-flakey-refresh.json b/devenv/dev-dashboards/live/live-flakey-refresh.json index c68dd267f9c..2fb5f98261f 100644 --- a/devenv/dev-dashboards/live/live-flakey-refresh.json +++ b/devenv/dev-dashboards/live/live-flakey-refresh.json @@ -1,377 +1,368 @@ { - "annotations": { - "list": [ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 220, + "links": [ + { + "asDropdown": false, + "icon": "external link", + "includeVars": false, + "keepTime": false, + "tags": ["live-tests"], + "targetBlank": false, + "title": "gdev live tests", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "liveNow": true, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 9, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "## Note the consistent refresh rate (liveNow = true)", + "mode": "markdown" + }, + "pluginVersion": "11.1.0-pre", + "type": "text" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 4, + "w": 11, + "x": 0, + "y": 2 + }, + "id": 2, + "options": { + "channel": { + "namespace": "devenv", + "path": "weather", + "scope": "stream" + }, + "display": "none", + "json": { + "hello": "world" + }, + "message": "weather,location=west,sensor=A temperature=82\nweather,location=east,sensor=A temperature=76", + "publish": "influx" + }, + "title": "Enter weather data", + "type": "live" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 13, + "x": 11, + "y": 2 + }, + "id": 4, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "11.1.0-pre", + "targets": [ { - "builtIn": 1, + "channel": "stream/devenv/weather", "datasource": { - "type": "grafana", - "uid": "-- Grafana --" + "type": "datasource", + "uid": "grafana" }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" + "queryType": "measurements", + "refId": "A" } - ] + ], + "title": "Weather (values)", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": true, + "labelsToFields": false, + "mode": "reduceFields", + "reducers": ["last"] + } + } + ], + "type": "table" }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 220, - "links": [ - { - "asDropdown": false, - "icon": "external link", - "includeVars": false, - "keepTime": false, - "tags": [ - "live-tests" - ], - "targetBlank": false, - "title": "gdev live tests", - "tooltip": "", - "type": "dashboards", - "url": "" - } - ], - "liveNow": true, - "panels": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 2, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 9, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "## Note the consistent refresh rate (liveNow = true)", - "mode": "markdown" - }, - "pluginVersion": "11.1.0-pre", - "type": "text" + { + "datasource": { + "type": "datasource", + "uid": "grafana" }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 4, - "w": 11, - "x": 0, - "y": 2 - }, - "id": 2, - "options": { - "channel": { - "namespace": "devenv", - "path": "weather", - "scope": "stream" + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" }, - "display": "none", - "json": { - "hello": "world" - }, - "message": "weather,location=west,sensor=A temperature=82\nweather,location=east,sensor=A temperature=76", - "publish": "influx" - }, - "title": "Enter weather data", - "type": "live" - }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 7, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "always", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 13, - "x": 11, - "y": 2 - }, - "id": 4, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "11.1.0-pre", - "targets": [ - { - "channel": "stream/devenv/weather", - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "queryType": "measurements", - "refId": "A" - } - ], - "title": "Weather (values)", - "transformations": [ - { - "id": "reduce", - "options": { - "includeTimeField": true, - "labelsToFields": false, - "mode": "reduceFields", - "reducers": [ - "last" - ] - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 7, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "always", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" + { + "color": "red", + "value": 80 } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 6 - }, - "id": 1, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + ] } }, - "targets": [ - { - "channel": "stream/devenv/weather", - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "queryType": "measurements", - "refId": "A" - } - ], - "title": "Manually entered weather points", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "channel": "stream/devenv/weather", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "measurements", + "refId": "A" + } + ], + "title": "Manually entered weather points", + "type": "timeseries" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 7, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "always", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 7, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "always", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } }, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 13 + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 13 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "id": 10, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "channel": "plugin/testdata/random-flakey-stream", + "datasource": { + "type": "datasource", + "uid": "grafana" }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "channel": "plugin/testdata/random-flakey-stream", - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "queryType": "measurements", - "refId": "A" - } - ], - "title": "Random flakey stream", - "type": "timeseries" - } - ], - "schemaVersion": 39, - "tags": [ - "gdev", - "live-tests" - ], - "templating": { - "list": [] - }, - "time": { - "from": "now-30s", - "to": "now" - }, - "timeRangeUpdatedDuringEditOrView": false, - "timepicker": {}, - "timezone": "browser", - "title": "Live flakey stream (w/ liveNow)", - "uid": "liveddluze", - "version": 4, - "weekStart": "" - } \ No newline at end of file + "queryType": "measurements", + "refId": "A" + } + ], + "title": "Random flakey stream", + "type": "timeseries" + } + ], + "schemaVersion": 39, + "tags": ["gdev", "live-tests"], + "templating": { + "list": [] + }, + "time": { + "from": "now-30s", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "browser", + "title": "Live flakey stream (w/ liveNow)", + "uid": "liveddluze", + "version": 4, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/live/live-flakey.json b/devenv/dev-dashboards/live/live-flakey.json index e8ae8a7d1b0..d7ff8d6cb0b 100644 --- a/devenv/dev-dashboards/live/live-flakey.json +++ b/devenv/dev-dashboards/live/live-flakey.json @@ -1,377 +1,368 @@ { - "annotations": { - "list": [ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 220, + "links": [ + { + "asDropdown": false, + "icon": "external link", + "includeVars": false, + "keepTime": false, + "tags": ["live-tests"], + "targetBlank": false, + "title": "gdev live tests", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 9, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false + }, + "content": "## Note the flakey refresh rate (liveNow = false). Also the time axis is not the same for all panels.", + "mode": "markdown" + }, + "pluginVersion": "11.1.0-pre", + "type": "text" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 4, + "w": 11, + "x": 0, + "y": 2 + }, + "id": 2, + "options": { + "channel": { + "namespace": "devenv", + "path": "weather", + "scope": "stream" + }, + "display": "none", + "json": { + "hello": "world" + }, + "message": "weather,location=west,sensor=A temperature=82\nweather,location=east,sensor=A temperature=76", + "publish": "influx" + }, + "title": "Enter weather data", + "type": "live" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 13, + "x": 11, + "y": 2 + }, + "id": 4, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "11.1.0-pre", + "targets": [ { - "builtIn": 1, + "channel": "stream/devenv/weather", "datasource": { - "type": "grafana", - "uid": "-- Grafana --" + "type": "datasource", + "uid": "grafana" }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" + "queryType": "measurements", + "refId": "A" } - ] + ], + "title": "Weather (values)", + "transformations": [ + { + "id": "reduce", + "options": { + "includeTimeField": true, + "labelsToFields": false, + "mode": "reduceFields", + "reducers": ["last"] + } + } + ], + "type": "table" }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 220, - "links": [ - { - "asDropdown": false, - "icon": "external link", - "includeVars": false, - "keepTime": false, - "tags": [ - "live-tests" - ], - "targetBlank": false, - "title": "gdev live tests", - "tooltip": "", - "type": "dashboards", - "url": "" - } - ], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 2, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 9, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "## Note the flakey refresh rate (liveNow = false). Also the time axis is not the same for all panels.", - "mode": "markdown" - }, - "pluginVersion": "11.1.0-pre", - "type": "text" + { + "datasource": { + "type": "datasource", + "uid": "grafana" }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 4, - "w": 11, - "x": 0, - "y": 2 - }, - "id": 2, - "options": { - "channel": { - "namespace": "devenv", - "path": "weather", - "scope": "stream" + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" }, - "display": "none", - "json": { - "hello": "world" - }, - "message": "weather,location=west,sensor=A temperature=82\nweather,location=east,sensor=A temperature=76", - "publish": "influx" - }, - "title": "Enter weather data", - "type": "live" - }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 7, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "always", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 13, - "x": 11, - "y": 2 - }, - "id": 4, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "11.1.0-pre", - "targets": [ - { - "channel": "stream/devenv/weather", - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "queryType": "measurements", - "refId": "A" - } - ], - "title": "Weather (values)", - "transformations": [ - { - "id": "reduce", - "options": { - "includeTimeField": true, - "labelsToFields": false, - "mode": "reduceFields", - "reducers": [ - "last" - ] - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 7, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "always", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" + { + "color": "red", + "value": 80 } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 6 - }, - "id": 1, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + ] } }, - "targets": [ - { - "channel": "stream/devenv/weather", - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "queryType": "measurements", - "refId": "A" - } - ], - "title": "Manually entered weather points", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "channel": "stream/devenv/weather", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "measurements", + "refId": "A" + } + ], + "title": "Manually entered weather points", + "type": "timeseries" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 7, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "always", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 7, + "scaleDistribution": { + "type": "linear" }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "showPoints": "always", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } }, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 13 + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 13 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "id": 10, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "channel": "plugin/testdata/random-flakey-stream", + "datasource": { + "type": "datasource", + "uid": "grafana" }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "channel": "plugin/testdata/random-flakey-stream", - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "queryType": "measurements", - "refId": "A" - } - ], - "title": "Random flakey stream", - "type": "timeseries" - } - ], - "schemaVersion": 39, - "tags": [ - "gdev", - "live-tests" - ], - "templating": { - "list": [] - }, - "time": { - "from": "now-30s", - "to": "now" - }, - "timeRangeUpdatedDuringEditOrView": false, - "timepicker": {}, - "timezone": "browser", - "title": "Live flakey stream", - "uid": "liveddluzy", - "version": 4, - "weekStart": "" - } \ No newline at end of file + "queryType": "measurements", + "refId": "A" + } + ], + "title": "Random flakey stream", + "type": "timeseries" + } + ], + "schemaVersion": 39, + "tags": ["gdev", "live-tests"], + "templating": { + "list": [] + }, + "time": { + "from": "now-30s", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "browser", + "title": "Live flakey stream", + "uid": "liveddluzy", + "version": 4, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/live/live-publish.json b/devenv/dev-dashboards/live/live-publish.json index 50269ad28e2..6121af7eea2 100644 --- a/devenv/dev-dashboards/live/live-publish.json +++ b/devenv/dev-dashboards/live/live-publish.json @@ -1,462 +1,453 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 209, - "links": [ + "annotations": { + "list": [ { - "asDropdown": false, - "icon": "external link", - "includeVars": false, - "keepTime": false, - "tags": [ - "live-tests" - ], - "targetBlank": false, - "title": "gdev live tests", - "tooltip": "", - "type": "dashboards", - "url": "" + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" } - ], - "panels": [ - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 2, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 9, - "options": { - "code": { - "language": "plaintext", - "showLineNumbers": false, - "showMiniMap": false - }, - "content": "## This dashboard requires alpha panels to be enabled!", - "mode": "markdown" - }, - "pluginVersion": "11.0.0-pre", - "type": "text" + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 209, + "links": [ + { + "asDropdown": false, + "icon": "external link", + "includeVars": false, + "keepTime": false, + "tags": ["live-tests"], + "targetBlank": false, + "title": "gdev live tests", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "grafana" }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 4, - "w": 15, - "x": 0, - "y": 2 - }, - "id": 2, - "options": { - "channel": { - "namespace": "devenv", - "path": "weather", - "scope": "stream" - }, - "display": "none", - "json": { - "hello": "world" - }, - "message": "weather,location=west,sensor=A temperature=82\nweather,location=east,sensor=A temperature=76", - "publish": "influx" - }, - "title": "Panel Title", - "type": "live" + "gridPos": { + "h": 2, + "w": 24, + "x": 0, + "y": 0 }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" + "id": 9, + "options": { + "code": { + "language": "plaintext", + "showLineNumbers": false, + "showMiniMap": false }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 9, - "x": 15, - "y": 2 - }, - "id": 4, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "11.0.0-pre", - "targets": [ - { - "channel": "stream/devenv/weather", - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "queryType": "measurements", - "refId": "A" - } - ], - "title": "Weather (values)", - "transformations": [ - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "time" - } - ] - } - } - ], - "type": "table" + "content": "## This dashboard requires alpha panels to be enabled!", + "mode": "markdown" }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 4, - "w": 15, - "x": 0, - "y": 6 - }, - "id": 5, - "options": { - "channel": { - "namespace": "devenv", - "path": "weather", - "scope": "stream" - }, - "display": "none", - "json": { - "hello": "world" - }, - "message": "weather,location=west,sensor=A temperature=90\nweather,location=east,sensor=A temperature=80", - "publish": "influx" - }, - "title": "Timeseries with multiple streaming queries", - "type": "live" + "pluginVersion": "11.0.0-pre", + "type": "text" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" + "gridPos": { + "h": 4, + "w": 15, + "x": 0, + "y": 2 + }, + "id": 2, + "options": { + "channel": { + "namespace": "devenv", + "path": "weather", + "scope": "stream" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "display": "none", + "json": { + "hello": "world" + }, + "message": "weather,location=west,sensor=A temperature=82\nweather,location=east,sensor=A temperature=76", + "publish": "influx" + }, + "title": "Panel Title", + "type": "live" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" + { + "color": "red", + "value": 80 } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 15, + "y": 2 + }, + "id": 4, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "11.0.0-pre", + "targets": [ + { + "channel": "stream/devenv/weather", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "measurements", + "refId": "A" + } + ], + "title": "Weather (values)", + "transformations": [ + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "time" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 4, + "w": 15, + "x": 0, + "y": 6 + }, + "id": 5, + "options": { + "channel": { + "namespace": "devenv", + "path": "weather", + "scope": "stream" + }, + "display": "none", + "json": { + "hello": "world" + }, + "message": "weather,location=west,sensor=A temperature=90\nweather,location=east,sensor=A temperature=80", + "publish": "influx" + }, + "title": "Timeseries with multiple streaming queries", + "type": "live" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 24, - "x": 0, - "y": 10 - }, - "id": 1, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "channel": "stream/devenv/weather", - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "queryType": "measurements", - "refId": "A" - }, - { - "channel": "stream/devenv/weatherX", - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "hide": false, - "queryType": "measurements", - "refId": "B" - } - ], - "title": "Panel Title", - "type": "timeseries" - }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "gridPos": { - "h": 4, - "w": 15, - "x": 0, - "y": 17 - }, - "id": 6, - "options": { - "channel": { - "namespace": "devenv", - "path": "weather", - "scope": "stream" - }, - "display": "none", - "json": { - "hello": "world" - }, - "message": "weatherX,location=west,sensor=X temperature=82\nweatherX,location=east,sensor=X temperature=76", - "publish": "influx" - }, - "title": "Panel Title", - "type": "live" - }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 9, - "x": 15, - "y": 17 - }, - "id": 7, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "11.0.0-pre", - "targets": [ - { - "channel": "stream/devenv/weatherX", - "datasource": { - "type": "datasource", - "uid": "grafana" - }, - "queryType": "measurements", - "refId": "A" + { + "color": "red", + "value": 80 + } + ] } - ], - "title": "WeatherX (values)", - "transformations": [ - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "time" - } - ] - } - } - ], - "type": "table" + }, + "overrides": [] }, - { - "datasource": { - "type": "datasource", - "uid": "grafana" + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "gridPos": { - "h": 4, - "w": 15, - "x": 0, - "y": 21 - }, - "id": 8, - "options": { - "channel": { - "namespace": "devenv", - "path": "weather", - "scope": "stream" + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "channel": "stream/devenv/weather", + "datasource": { + "type": "datasource", + "uid": "grafana" }, - "display": "none", - "json": { - "hello": "world" - }, - "message": "weatherX,location=west,sensor=X temperature=90\nweatherX,location=east,sensor=X temperature=22", - "publish": "influx" + "queryType": "measurements", + "refId": "A" }, - "title": "Panel Title", - "type": "live" - } - ], - "schemaVersion": 39, - "tags": [ - "gdev", - "live-tests" - ], - "templating": { - "list": [] + { + "channel": "stream/devenv/weatherX", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "hide": false, + "queryType": "measurements", + "refId": "B" + } + ], + "title": "Panel Title", + "type": "timeseries" }, - "time": { - "from": "now-1m", - "to": "now" + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 4, + "w": 15, + "x": 0, + "y": 17 + }, + "id": 6, + "options": { + "channel": { + "namespace": "devenv", + "path": "weather", + "scope": "stream" + }, + "display": "none", + "json": { + "hello": "world" + }, + "message": "weatherX,location=west,sensor=X temperature=82\nweatherX,location=east,sensor=X temperature=76", + "publish": "influx" + }, + "title": "Panel Title", + "type": "live" }, - "timepicker": {}, - "timezone": "browser", - "title": "Live publish test", - "uid": "addoomtlivedev", - "version": 17, - "weekStart": "" - } \ No newline at end of file + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 15, + "y": 17 + }, + "id": 7, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "11.0.0-pre", + "targets": [ + { + "channel": "stream/devenv/weatherX", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "queryType": "measurements", + "refId": "A" + } + ], + "title": "WeatherX (values)", + "transformations": [ + { + "id": "sortBy", + "options": { + "fields": {}, + "sort": [ + { + "desc": true, + "field": "time" + } + ] + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "gridPos": { + "h": 4, + "w": 15, + "x": 0, + "y": 21 + }, + "id": 8, + "options": { + "channel": { + "namespace": "devenv", + "path": "weather", + "scope": "stream" + }, + "display": "none", + "json": { + "hello": "world" + }, + "message": "weatherX,location=west,sensor=X temperature=90\nweatherX,location=east,sensor=X temperature=22", + "publish": "influx" + }, + "title": "Panel Title", + "type": "live" + } + ], + "schemaVersion": 39, + "tags": ["gdev", "live-tests"], + "templating": { + "list": [] + }, + "time": { + "from": "now-1m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Live publish test", + "uid": "addoomtlivedev", + "version": 17, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/live/live-streams.json b/devenv/dev-dashboards/live/live-streams.json index f7b178ec3b7..f7598ecbd99 100644 --- a/devenv/dev-dashboards/live/live-streams.json +++ b/devenv/dev-dashboards/live/live-streams.json @@ -1,795 +1,758 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 218, + "links": [ + { + "asDropdown": false, + "icon": "external link", + "includeVars": false, + "keepTime": false, + "tags": ["live-tests"], + "targetBlank": false, + "title": "gdev live tests", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "liveNow": true, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.lineWidth", + "value": 3 + }, + { + "id": "color", + "value": { + "fixedColor": "super-light-blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Max" + }, + "properties": [ + { + "id": "custom.fillBelowTo", + "value": "Min" + }, + { + "id": "custom.fillOpacity", + "value": 72 + }, + { + "id": "color", + "value": { + "fixedColor": "dark-blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Min" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 13, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" } - ] + }, + "targets": [ + { + "channel": "random-20Hz-stream", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "live" + } + ], + "title": "Random 20hs stream", + "type": "timeseries" }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 218, - "links": [ - { - "asDropdown": false, - "icon": "external link", - "includeVars": false, - "keepTime": false, - "tags": [ - "live-tests" - ], - "targetBlank": false, - "title": "gdev live tests", - "tooltip": "", - "type": "dashboards", - "url": "" - } - ], - "liveNow": true, - "panels": [ - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" + { + "color": "red", + "value": 80 } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Value" - }, - "properties": [ - { - "id": "custom.lineWidth", - "value": 3 - }, - { - "id": "color", - "value": { - "fixedColor": "super-light-blue", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Max" - }, - "properties": [ - { - "id": "custom.fillBelowTo", - "value": "Min" - }, - { - "id": "custom.fillOpacity", - "value": 72 - }, - { - "id": "color", - "value": { - "fixedColor": "dark-blue", - "mode": "fixed" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Min" - }, - "properties": [ - { - "id": "color", - "value": { - "fixedColor": "dark-blue", - "mode": "fixed" - } - } - ] - } - ] - }, - "gridPos": { - "h": 7, - "w": 13, - "x": 0, - "y": 0 - }, - "id": 1, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" + ] } }, - "targets": [ - { - "channel": "random-20Hz-stream", - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "live" - } - ], - "title": "Random 20hs stream", - "type": "timeseries" + "overrides": [] }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 7, - "x": 13, - "y": 0 - }, - "id": 5, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Time" - } - ] - }, - "pluginVersion": "11.1.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 1, - "refId": "A" - } - ], - "title": "Panel Title", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "Time", - "Value" - ] - } - } - } - ], - "type": "table" + "gridPos": { + "h": 7, + "w": 7, + "x": 13, + "y": 0 }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" + "id": 5, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "blue", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 4, - "x": 20, - "y": 0 - }, - "id": 3, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.1.0-pre", - "targets": [ + "showHeader": true, + "sortBy": [ { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 1, - "refId": "A" + "desc": true, + "displayName": "Time" } - ], - "title": "Stats", - "transformations": [ - { - "id": "reduce", - "options": { - "reducers": [ - "lastNotNull", - "min", - "max", - "count" - ] - } - } - ], - "type": "stat" + ] }, - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } + "pluginVersion": "11.1.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 13, - "x": 0, - "y": 7 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "channel": "random-2s-stream", - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "live" - } - ], - "title": "Random 2s stream", - "type": "timeseries" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 7, - "x": 13, - "y": 7 - }, - "id": 6, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Time" - } - ] - }, - "pluginVersion": "11.1.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A" - } - ], - "title": "Panel Title", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "Time", - "Value" - ] - } + "panelId": 1, + "refId": "A" + } + ], + "title": "Panel Title", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["Time", "Value"] } } - ], - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "blue", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 4, - "x": 20, - "y": 7 - }, - "id": 4, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.1.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A" - } - ], - "title": "Stats", - "transformations": [ - { - "id": "reduce", - "options": { - "reducers": [ - "lastNotNull", - "min", - "max", - "count" - ] - } - } - ], - "type": "stat" - }, - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 13, - "x": 0, - "y": 14 - }, - "id": 7, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "channel": "random-labeled-stream", - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "live" - } - ], - "title": "Random Labeled streams", - "type": "timeseries" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 11, - "x": 13, - "y": 14 - }, - "id": 8, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Time" - } - ] - }, - "pluginVersion": "11.1.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 7, - "refId": "A" - } - ], - "title": "Labeled structure", - "type": "table" - } - ], - "schemaVersion": 39, - "tags": [ - "gdev", - "live-tests" - ], - "templating": { - "list": [] + } + ], + "type": "table" }, - "time": { - "from": "now-1m", - "to": "now" + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 4, + "x": 20, + "y": 0 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 1, + "refId": "A" + } + ], + "title": "Stats", + "transformations": [ + { + "id": "reduce", + "options": { + "reducers": ["lastNotNull", "min", "max", "count"] + } + } + ], + "type": "stat" }, - "timeRangeUpdatedDuringEditOrView": false, - "timepicker": {}, - "timezone": "browser", - "title": "Live streaming examples", - "uid": "liveb94wbb", - "version": 9, - "weekStart": "" - } \ No newline at end of file + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 13, + "x": 0, + "y": 7 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "channel": "random-2s-stream", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "live" + } + ], + "title": "Random 2s stream", + "type": "timeseries" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 7, + "x": 13, + "y": 7 + }, + "id": 6, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Time" + } + ] + }, + "pluginVersion": "11.1.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "title": "Panel Title", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": ["Time", "Value"] + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 4, + "x": 20, + "y": 7 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "title": "Stats", + "transformations": [ + { + "id": "reduce", + "options": { + "reducers": ["lastNotNull", "min", "max", "count"] + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 13, + "x": 0, + "y": 14 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "channel": "random-labeled-stream", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "live" + } + ], + "title": "Random Labeled streams", + "type": "timeseries" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 11, + "x": 13, + "y": 14 + }, + "id": 8, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Time" + } + ] + }, + "pluginVersion": "11.1.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 7, + "refId": "A" + } + ], + "title": "Labeled structure", + "type": "table" + } + ], + "schemaVersion": 39, + "tags": ["gdev", "live-tests"], + "templating": { + "list": [] + }, + "time": { + "from": "now-1m", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "browser", + "title": "Live streaming examples", + "uid": "liveb94wbb", + "version": 9, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/migrations/migrations.json b/devenv/dev-dashboards/migrations/migrations.json index d872f4e6893..4c2160e1f26 100644 --- a/devenv/dev-dashboards/migrations/migrations.json +++ b/devenv/dev-dashboards/migrations/migrations.json @@ -195,7 +195,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "unit": "short" @@ -249,8 +251,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -288,7 +289,9 @@ } }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 11, "w": 8, @@ -309,8 +312,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A" } @@ -323,7 +325,9 @@ "bars": true, "dashLength": 10, "dashes": false, - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "unit": "short" @@ -377,8 +381,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"typeVersion\": [\n 0,\n 0\n ],\n \"custom\": {\n \"customStat\": 10\n }\n },\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {\n \"interval\": 3600000\n }\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"labels\": {\n \"pod\": \"A-pod\"\n },\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1727107111901,\n 1727110711901,\n 1727114311901,\n 1727117911901,\n 1727121511901,\n 1727125111901\n ],\n [\n 1.907286825122581,\n 2.260951647569786,\n 1.887442338051216,\n 2.1526144400893514,\n 1.7287721375237766,\n 1.7262902137793208\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"typeVersion\": [\n 0,\n 0\n ],\n \"custom\": {\n \"customStat\": 10\n }\n },\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {\n \"interval\": 3600000\n }\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"labels\": {\n \"pod\": \"A-pod1\"\n },\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1727107111901,\n 1727110711901,\n 1727114311901,\n 1727117911901,\n 1727121511901,\n 1727125111901\n ],\n [\n 1.907286825122581,\n 1.589539045095202,\n 1.5914283506847613,\n 1.8976990616650726,\n 1.758223085999124,\n 2.2294649594813816\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"typeVersion\": [\n 0,\n 0\n ],\n \"custom\": {\n \"customStat\": 10\n }\n },\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {\n \"interval\": 3600000\n }\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"labels\": {\n \"pod\": \"A-pod2\"\n },\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1727107111901,\n 1727110711901,\n 1727114311901,\n 1727117911901,\n 1727121511901,\n 1727125111901\n ],\n [\n 1.907286825122581,\n 2.0914263380328766,\n 1.8164545521094575,\n 1.621111084665713,\n 1.3902653996444705,\n 1.482803315949775\n ]\n ]\n }\n }\n]", "refId": "A", @@ -397,9 +400,7 @@ "xaxis": { "mode": "series", "show": true, - "values": [ - "total" - ] + "values": ["total"] }, "yaxes": [ { @@ -420,7 +421,9 @@ } }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 11, "w": 8, @@ -441,8 +444,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A" } @@ -533,9 +535,7 @@ "xaxis": { "mode": "series", "show": true, - "values": [ - "total" - ] + "values": ["total"] }, "yaxes": [ { @@ -556,7 +556,9 @@ } }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 11, "w": 8, @@ -577,8 +579,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A" } @@ -591,7 +592,9 @@ "bars": true, "dashLength": 10, "dashes": false, - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "unit": "short" @@ -645,8 +648,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -686,7 +688,9 @@ } }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 11, "w": 8, @@ -707,8 +711,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A" } @@ -718,7 +721,9 @@ }, { "columns": [], - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fontSize": "100%", "gridPos": { "h": 10, @@ -732,9 +737,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true, @@ -757,11 +760,7 @@ { "alias": "", "align": "right", - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], "decimals": 2, "pattern": "/.*/", "thresholds": [], @@ -772,8 +771,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk_table" @@ -792,7 +790,9 @@ "type": "table-old" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 10, "w": 8, @@ -813,8 +813,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A" } @@ -825,12 +824,10 @@ { "colorBackground": false, "colorValue": true, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": { "type": "testdata" }, + "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], + "datasource": { + "type": "grafana-testdata-datasource" + }, "format": "areaF2", "gauge": { "maxValue": 100, @@ -880,8 +877,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A" } @@ -900,7 +896,9 @@ "valueName": "avg" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "mappings": [ @@ -945,9 +943,7 @@ "justifyMode": "auto", "orientation": "horizontal", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -959,8 +955,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A" } @@ -969,7 +964,9 @@ "type": "stat" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 8, "w": 8, @@ -990,8 +987,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A" } @@ -1000,7 +996,9 @@ "type": "text" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 10, "w": 16, @@ -1016,9 +1014,7 @@ }, "pieType": "pie", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -1031,8 +1027,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk_table" @@ -1050,7 +1045,9 @@ "type": "grafana-piechart-panel" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 10, "w": 8, @@ -1071,8 +1068,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A" } @@ -1083,14 +1079,9 @@ { "circleMaxSize": 30, "circleMinSize": 2, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "decimals": 0, "esMetric": "Count", @@ -1194,8 +1185,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -1213,9 +1203,7 @@ { "id": "reduce", "options": { - "reducers": [ - "sum" - ] + "reducers": ["sum"] } } ], @@ -1225,7 +1213,9 @@ "valueName": "total" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 10, "w": 8, @@ -1246,8 +1236,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A" } @@ -1258,11 +1247,7 @@ ], "refresh": "", "schemaVersion": 39, - "tags": [ - "gdev", - "migrations", - "angular" - ], + "tags": ["gdev", "migrations", "angular"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-barchart/barchart-autosizing.json b/devenv/dev-dashboards/panel-barchart/barchart-autosizing.json index d484fabcc11..dce9167e212 100644 --- a/devenv/dev-dashboards/panel-barchart/barchart-autosizing.json +++ b/devenv/dev-dashboards/panel-barchart/barchart-autosizing.json @@ -29,7 +29,7 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -79,9 +79,7 @@ "barWidth": 1, "groupWidth": 0.82, "legend": { - "calcs": [ - "max" - ], + "calcs": ["max"], "displayMode": "list", "placement": "right" }, @@ -107,7 +105,7 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "description": "Should be smaller given the longer value", "fieldConfig": { "defaults": { @@ -159,9 +157,7 @@ "barWidth": 1, "groupWidth": 0.82, "legend": { - "calcs": [ - "max" - ], + "calcs": ["max"], "displayMode": "list", "placement": "right" }, @@ -187,7 +183,7 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -237,9 +233,7 @@ "barWidth": 1, "groupWidth": 0.89, "legend": { - "calcs": [ - "max" - ], + "calcs": ["max"], "displayMode": "list", "placement": "right" }, @@ -265,7 +259,7 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -315,9 +309,7 @@ "barWidth": 1, "groupWidth": 0.89, "legend": { - "calcs": [ - "max" - ], + "calcs": ["max"], "displayMode": "list", "placement": "right" }, @@ -424,7 +416,7 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -475,9 +467,7 @@ "barWidth": 1, "groupWidth": 0.82, "legend": { - "calcs": [ - "max" - ], + "calcs": ["max"], "displayMode": "list", "placement": "right" }, @@ -503,7 +493,7 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { "defaults": { @@ -554,9 +544,7 @@ "barWidth": 1, "groupWidth": 0.89, "legend": { - "calcs": [ - "max" - ], + "calcs": ["max"], "displayMode": "list", "placement": "right" }, @@ -584,12 +572,7 @@ ], "refresh": "", "schemaVersion": 36, - "tags": [ - "gdev", - "panel-tests", - "barchart", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "barchart", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-barchart/barchart-label-rotation-skipping.json b/devenv/dev-dashboards/panel-barchart/barchart-label-rotation-skipping.json index ee99245953b..5695faa2c6f 100644 --- a/devenv/dev-dashboards/panel-barchart/barchart-label-rotation-skipping.json +++ b/devenv/dev-dashboards/panel-barchart/barchart-label-rotation-skipping.json @@ -1,319 +1,309 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 530, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "fillOpacity": 80, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "decmbytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 14, - "w": 11, - "x": 0, - "y": 0 - }, - "id": 2, - "maxDataPoints": 30, - "options": { - "barRadius": 0, - "barWidth": 0.97, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "orientation": "vertical", - "showValue": "auto", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelMaxLength": 6, - "xTickLabelRotation": 45, - "xTickLabelSpacing": 100 - }, - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"name\": \"label\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {\n \"interval\": 600000\n }\n },\n {\n \"name\": \"A-series\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"labels\": {},\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n \"acquisition\",\n \"extension\",\n \"conductor\",\n \"authorise\",\n \"architect\",\n \"illusion\",\n \"congress\",\n \"highlight\",\n \"partnership\",\n \"understanding\",\n \"disagreement\",\n \"personality\",\n \"commerce\",\n \"systematic\",\n \"hesitate\",\n \"business\",\n \"manufacture\",\n \"incredible\",\n \"constitutional\",\n \"prevalence\",\n \"professor\",\n \"entitlement\",\n \"cooperation\",\n \"sickness\",\n \"contrast\",\n \"reference\",\n \"audience\",\n \"discount\",\n \"apparatus\",\n \"disturbance\",\n \"automatic\",\n \"refrigerator\",\n \"elaborate\",\n \"sympathetic\",\n \"integration\",\n \"president\"\n ],\n [\n 306.78931659492116,\n 200.00696051101917,\n 164.90889283973593,\n 518.9385023737021,\n 999.9040675564702,\n 613.9689830172349,\n 773.2337077340269,\n 317.47395634701644,\n 748.3318338316539,\n 606.8039493787173,\n 426.27771317792866,\n 376.47735643253924,\n 66.30635081800493,\n 401.70654338415505,\n 108.86259550477234,\n 182.40284186231278,\n 867.7047958572101,\n 959.3957783599242,\n 396.7606089549935,\n 455.9625595614323,\n 685.4792456298062,\n 368.6567303946707,\n 157.06596562976327,\n 59.54120602048763,\n 406.72723615743973,\n 440.18247585615575,\n 516.0267558264891,\n 258.76006051667315,\n 952.966531725171,\n 554.8746357628739,\n 86.7279280805682,\n 781.2422516386563,\n 754.2723802427706,\n 435.0305712850233,\n 384.43181614983,\n 459.04164596738127\n ]\n ]\n }\n }\n]", - "refId": "A", - "scenarioId": "raw_frame" - } - ], - "title": "Panel Title", - "type": "barchart" + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 530, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource" }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "axisSoftMin": 0, - "fillOpacity": 80, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineWidth": 0, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" - } + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" }, - "decimals": 7, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] + "thresholdsStyle": { + "mode": "off" } }, - "overrides": [] - }, - "gridPos": { - "h": 14, - "w": 13, - "x": 11, - "y": 0 - }, - "id": 5, - "options": { - "barRadius": 0, - "barWidth": 1, - "groupWidth": 0.82, - "legend": { - "calcs": [ - "max" - ], - "displayMode": "list", - "placement": "right", - "showLegend": true - }, - "orientation": "horizontal", - "showValue": "auto", - "stacking": "none", - "text": {}, - "tooltip": { - "mode": "single", - "sort": "none" - }, - "xTickLabelRotation": 45, - "xTickLabelSpacing": 0 - }, - "targets": [ - { - "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, -5\nLondon, 10, 1\nLong value, 15,10", - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Auto sizing & auto show values", - "type": "barchart" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "fillOpacity": 80, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" }, - "lineWidth": 1, - "scaleDistribution": { - "type": "linear" - }, - "thresholdsStyle": { - "mode": "off" + { + "color": "red", + "value": 80 } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "decmbytes" + ] }, - "overrides": [] + "unit": "decmbytes" }, - "gridPos": { - "h": 18, - "w": 24, - "x": 0, - "y": 14 + "overrides": [] + }, + "gridPos": { + "h": 14, + "w": 11, + "x": 0, + "y": 0 + }, + "id": 2, + "maxDataPoints": 30, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true }, - "id": 3, - "maxDataPoints": 20, - "options": { - "barRadius": 0, - "barWidth": 0.97, - "groupWidth": 0.7, - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true + "orientation": "vertical", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelMaxLength": 6, + "xTickLabelRotation": 45, + "xTickLabelSpacing": 100 + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" }, - "orientation": "horizontal", - "showValue": "auto", - "stacking": "none", - "tooltip": { - "mode": "single", - "sort": "none" + "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"name\": \"label\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {\n \"interval\": 600000\n }\n },\n {\n \"name\": \"A-series\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"labels\": {},\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n \"acquisition\",\n \"extension\",\n \"conductor\",\n \"authorise\",\n \"architect\",\n \"illusion\",\n \"congress\",\n \"highlight\",\n \"partnership\",\n \"understanding\",\n \"disagreement\",\n \"personality\",\n \"commerce\",\n \"systematic\",\n \"hesitate\",\n \"business\",\n \"manufacture\",\n \"incredible\",\n \"constitutional\",\n \"prevalence\",\n \"professor\",\n \"entitlement\",\n \"cooperation\",\n \"sickness\",\n \"contrast\",\n \"reference\",\n \"audience\",\n \"discount\",\n \"apparatus\",\n \"disturbance\",\n \"automatic\",\n \"refrigerator\",\n \"elaborate\",\n \"sympathetic\",\n \"integration\",\n \"president\"\n ],\n [\n 306.78931659492116,\n 200.00696051101917,\n 164.90889283973593,\n 518.9385023737021,\n 999.9040675564702,\n 613.9689830172349,\n 773.2337077340269,\n 317.47395634701644,\n 748.3318338316539,\n 606.8039493787173,\n 426.27771317792866,\n 376.47735643253924,\n 66.30635081800493,\n 401.70654338415505,\n 108.86259550477234,\n 182.40284186231278,\n 867.7047958572101,\n 959.3957783599242,\n 396.7606089549935,\n 455.9625595614323,\n 685.4792456298062,\n 368.6567303946707,\n 157.06596562976327,\n 59.54120602048763,\n 406.72723615743973,\n 440.18247585615575,\n 516.0267558264891,\n 258.76006051667315,\n 952.966531725171,\n 554.8746357628739,\n 86.7279280805682,\n 781.2422516386563,\n 754.2723802427706,\n 435.0305712850233,\n 384.43181614983,\n 459.04164596738127\n ]\n ]\n }\n }\n]", + "refId": "A", + "scenarioId": "raw_frame" + } + ], + "title": "Panel Title", + "type": "barchart" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" }, - "xTickLabelMaxLength": 5, - "xTickLabelRotation": 45, - "xTickLabelSpacing": 100 - }, - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "axisSoftMin": 0, + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false }, - "panelId": 2, - "refId": "A" + "lineWidth": 0, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 7, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] } - ], - "title": "Panel Title", - "type": "barchart" - } - ], - "schemaVersion": 37, - "tags": [ - "gdev", - "panel-tests", - "barchart", - "graph-ng" - ], - "templating": { - "list": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 14, + "w": 13, + "x": 11, + "y": 0 + }, + "id": 5, + "options": { + "barRadius": 0, + "barWidth": 1, + "groupWidth": 0.82, + "legend": { + "calcs": ["max"], + "displayMode": "list", + "placement": "right", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "text": {}, + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelRotation": 45, + "xTickLabelSpacing": 0 + }, + "targets": [ + { + "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, -5\nLondon, 10, 1\nLong value, 15,10", + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Auto sizing & auto show values", + "type": "barchart" }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "BarChart - Label Rotation & Skipping", - "uid": "xCmMwXdVz", - "version": 20, - "weekStart": "" - } + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "fillOpacity": 80, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1, + "scaleDistribution": { + "type": "linear" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decmbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 18, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 3, + "maxDataPoints": 20, + "options": { + "barRadius": 0, + "barWidth": 0.97, + "groupWidth": 0.7, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "orientation": "horizontal", + "showValue": "auto", + "stacking": "none", + "tooltip": { + "mode": "single", + "sort": "none" + }, + "xTickLabelMaxLength": 5, + "xTickLabelRotation": 45, + "xTickLabelSpacing": 100 + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "title": "Panel Title", + "type": "barchart" + } + ], + "schemaVersion": 37, + "tags": ["gdev", "panel-tests", "barchart", "graph-ng"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "BarChart - Label Rotation & Skipping", + "uid": "xCmMwXdVz", + "version": 20, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/panel-barchart/barchart-series-toggle.json b/devenv/dev-dashboards/panel-barchart/barchart-series-toggle.json index 8264f5a0315..d58523b07ea 100644 --- a/devenv/dev-dashboards/panel-barchart/barchart-series-toggle.json +++ b/devenv/dev-dashboards/panel-barchart/barchart-series-toggle.json @@ -24,8 +24,7 @@ "panels": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -104,8 +103,7 @@ { "csvContent": "Name,Stat1\nStockholm, 10\nNew York, 19\nLondon, 10\nNegative, 15\nLong value, 15", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -116,8 +114,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -196,8 +193,7 @@ { "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, 5\nLondon, 10, 1\nNegative, 15, 5\nLong value, 15,10", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -208,8 +204,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -288,8 +283,7 @@ { "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, 5\nLondon, 10, 1\nNegative, 15, 5\nLong value, 15,10", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -300,8 +294,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -380,8 +373,7 @@ { "csvContent": "Name,Stat1,Stat2\nStockholm, 10, 15\nNew York, 19, 5\nLondon, 10, 1\nNegative, 15, 5\nLong value, 15,10", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -392,12 +384,7 @@ } ], "schemaVersion": 39, - "tags": [ - "gdev", - "panel-tests", - "barchart", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "barchart", "graph-ng"], "templating": { "list": [] }, @@ -411,4 +398,4 @@ "uid": "adoero0hbka9sf", "version": 4, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-barchart/barchart-thresholds-mappings.json b/devenv/dev-dashboards/panel-barchart/barchart-thresholds-mappings.json index 81138940b74..7ff778a0353 100644 --- a/devenv/dev-dashboards/panel-barchart/barchart-thresholds-mappings.json +++ b/devenv/dev-dashboards/panel-barchart/barchart-thresholds-mappings.json @@ -28,7 +28,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -110,8 +112,7 @@ { "csvContent": "label,value\nx,3\na,10\nb,20\nc,30", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -121,7 +122,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -227,8 +230,7 @@ { "csvContent": "label,value\nx,3\na,10\nb,20\nc,30", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -238,7 +240,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -307,8 +311,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"name\": \"label\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"int64\",\n \"nullable\": true\n },\n \"config\": {\n \"thresholds\": {\n \"mode\": \"absolute\",\n \"steps\": [\n {\n \"color\": \"green\",\n \"value\": null\n },\n {\n \"color\": \"#EAB839\",\n \"value\": 10\n },\n {\n \"color\": \"red\",\n \"value\": 15\n },\n {\n \"color\": \"#6ED0E0\",\n \"value\": 25\n }\n ]\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n \"x\",\n \"a\",\n \"b\",\n \"c\"\n ],\n [\n 3,\n 10,\n 20,\n 30\n ]\n ]\n }\n }\n]", "refId": "A", @@ -319,7 +322,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -389,8 +394,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"name\": \"label\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"int64\",\n \"nullable\": true\n },\n \"config\": {\n \"thresholds\": {\n \"mode\": \"absolute\",\n \"steps\": [\n {\n \"color\": \"green\",\n \"value\": null\n },\n {\n \"color\": \"#EAB839\",\n \"value\": 10\n },\n {\n \"color\": \"red\",\n \"value\": 15\n },\n {\n \"color\": \"#6ED0E0\",\n \"value\": 25\n }\n ]\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n \"x\",\n \"a\",\n \"b\",\n \"c\"\n ],\n [\n 3,\n 10,\n 20,\n 30\n ]\n ]\n }\n }\n]", "refId": "A", @@ -401,7 +405,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -516,8 +522,7 @@ { "csvContent": "label,value\nx,3\na,10\nb,20\nc,30", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -527,7 +532,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -655,8 +662,7 @@ { "csvContent": "label,value\nx,3\na,10\nb,20\nc,30", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -666,7 +672,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -741,16 +749,14 @@ { "csvContent": "label,value\nx,3\na,10\nb,20\nc,30", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "max": 30, @@ -784,7 +790,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -913,8 +921,7 @@ { "csvContent": "label,value\nx,3\na,10\nb,20\nc,30", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -924,7 +931,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -997,8 +1006,7 @@ { "csvContent": "label,value\nx,3\na,10\nb,20\nc,30", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -1008,7 +1016,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1081,8 +1091,7 @@ { "csvContent": "label,value\nx,3\na,10\nb,20\nc,30", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -1092,7 +1101,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1165,8 +1176,7 @@ { "csvContent": "label,value\nx,3\na,10\nb,20\nc,30", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -1176,7 +1186,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1279,8 +1291,7 @@ { "csvContent": "label,value\nx,3\na,10\nb,20\nc,30", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -1291,12 +1302,7 @@ } ], "schemaVersion": 38, - "tags": [ - "gdev", - "panel-tests", - "barchart", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "barchart", "graph-ng"], "templating": { "list": [] }, @@ -1310,4 +1316,4 @@ "uid": "2I2uMSB7z", "version": 7, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-barchart/barchart-tooltips-legends.json b/devenv/dev-dashboards/panel-barchart/barchart-tooltips-legends.json index 7dc1b0ee08c..c99a5a7903b 100644 --- a/devenv/dev-dashboards/panel-barchart/barchart-tooltips-legends.json +++ b/devenv/dev-dashboards/panel-barchart/barchart-tooltips-legends.json @@ -163,8 +163,7 @@ { "csvContent": "id,column1,column2\r\nA,1,2", "datasource": { - "type": "testdata", - "uid": "o63ntNT4z" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -172,8 +171,7 @@ { "csvContent": "id,column1,column2\r\nB,2,1", "datasource": { - "type": "testdata", - "uid": "o63ntNT4z" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "B", @@ -318,8 +316,7 @@ { "csvContent": "id,column1,column2\r\nA,1,2", "datasource": { - "type": "testdata", - "uid": "o63ntNT4z" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -327,8 +324,7 @@ { "csvContent": "id,column1,column2\r\nB,2,1", "datasource": { - "type": "testdata", - "uid": "o63ntNT4z" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "B", @@ -358,7 +354,9 @@ "type": "row" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -451,8 +449,7 @@ { "csvContent": "id, field 1, field 2, field 3\na, 20, 30, 40\nb, 40, 50, 60", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -462,7 +459,9 @@ "type": "barchart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -555,8 +554,7 @@ { "csvContent": "id, field 1, field 2, field 3\na, 20, 30, 40\nb, 40, 50, 60", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -580,8 +578,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -640,12 +637,7 @@ "fullHighlight": false, "groupWidth": 0.7, "legend": { - "calcs": [ - "min", - "max", - "mean", - "variance" - ], + "calcs": ["min", "max", "mean", "variance"], "displayMode": "table", "placement": "bottom", "showLegend": true @@ -664,8 +656,7 @@ { "csvContent": "time, value\n100000, 10\n200000, 20\n300000, 30\n400000, 40", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -673,8 +664,7 @@ { "csvContent": "time, value\n100000, 500\n200000, 220\n300000, 10\n400000, 550", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "B", @@ -686,8 +676,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -779,8 +768,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"fields\": [\n {\n \"name\": \"TimeGenerated\",\n \"type\": \"time\",\n \"values\": [\n 1687200000000\n ]\n },\n {\n \"name\": \"Total\",\n \"type\": \"number\",\n \"values\": [\n 200\n ],\n \"labels\": {\n \"name\": \"A\"\n }\n },\n {\n \"name\": \"Total\",\n \"type\": \"number\",\n \"values\": [\n 180\n ],\n \"labels\": {\n \"name\": \"B\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"custom\": {\n \"azureColumnTypes\": [\n \"string\",\n \"datetime\",\n \"long\"\n ]\n },\n \"type\": \"timeseries-wide\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n }\n]", "refId": "A", @@ -792,8 +780,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -869,8 +856,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"config\": {},\n \"values\": [\n 1700488200000,\n 1700488500000,\n 1700488800000,\n 1700489100000,\n 1700489400000\n ]\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"config\": {\n \"displayNameFromDS\": \"f141\"\n },\n \"values\": [\n 11834,\n 26195,\n 27496,\n 21417\n ],\n \"labels\": {\n \"nodename\": \"z141\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"timeseries-multi\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n },\n {\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"config\": {},\n \"values\": [\n 1700488200000,\n 1700488500000,\n 1700488800000,\n 1700489100000,\n 1700489400000\n ]\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"config\": {\n \"displayNameFromDS\": \"f131\"\n },\n \"values\": [\n 11042,\n 26646,\n 27120,\n 21508,\n 19905\n ],\n \"labels\": {\n \"nodename\": \"t131\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"timeseries-multi\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n },\n {\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"config\": {},\n \"values\": [\n 1700488200000,\n 1700488500000,\n 1700488800000,\n 1700489100000,\n 1700489400000\n ]\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"config\": {\n \"displayNameFromDS\": \"f121\"\n },\n \"values\": [\n 12095,\n 26718,\n 26907,\n 21483,\n 20272\n ],\n \"labels\": {\n \"nodename\": \"k121\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"timeseries-multi\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n },\n {\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"config\": {},\n \"values\": [\n 1700488200000,\n 1700488500000,\n 1700488800000,\n 1700489100000,\n 1700489400000\n ]\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"config\": {\n \"displayNameFromDS\": \"f111\"\n },\n \"values\": [\n 641,\n 1015,\n 791,\n 752,\n 758\n ],\n \"labels\": {\n \"nodename\": \"n111\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"timeseries-multi\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n },\n {\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"config\": {},\n \"values\": [\n 1700488200000,\n 1700488500000,\n 1700488800000,\n 1700489100000,\n 1700489400000\n ]\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"config\": {\n \"displayNameFromDS\": \"f102\"\n },\n \"values\": [\n 101,\n 167,\n 205,\n 181,\n 172\n ],\n \"labels\": {\n \"nodename\": \"a102\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"timeseries-multi\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n },\n {\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"config\": {},\n \"values\": [\n 1700488200000,\n 1700488500000,\n 1700488800000,\n 1700489100000,\n 1700489400000\n ]\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"config\": {\n \"displayNameFromDS\": \"f100\"\n },\n \"values\": [\n 89,\n 137,\n 161,\n 150,\n 161\n ],\n \"labels\": {\n \"nodename\": \"v100\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"timeseries-multi\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n },\n {\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"config\": {},\n \"values\": [\n 1700488200000,\n 1700488500000,\n 1700488800000,\n 1700489100000,\n 1700489400000\n ]\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"config\": {\n \"displayNameFromDS\": \"d141\"\n },\n \"values\": [\n 11616,\n 26053,\n 25586,\n 21023,\n 19787\n ],\n \"labels\": {\n \"nodename\": \"e141\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"timeseries-multi\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n },\n {\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"config\": {},\n \"values\": [\n 1700488200000,\n 1700488500000,\n 1700488800000,\n 1700489100000,\n 1700489400000\n ]\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"config\": {\n \"displayNameFromDS\": \"d131\"\n },\n \"values\": [\n 11682,\n 26851,\n 26985,\n 22475,\n 20852\n ],\n \"labels\": {\n \"nodename\": \"r131\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"timeseries-multi\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n },\n {\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"config\": {},\n \"values\": [\n 1700488200000,\n 1700488500000,\n 1700488800000,\n 1700489100000,\n 1700489400000\n ]\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"config\": {\n \"displayNameFromDS\": \"d121\"\n },\n \"values\": [\n 12108,\n 26379,\n 25734,\n 20145,\n 19310\n ],\n \"labels\": {\n \"nodename\": \"g121\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"timeseries-multi\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n },\n {\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"config\": {},\n \"values\": [\n 1700488200000,\n 1700488500000,\n 1700488800000,\n 1700489100000,\n 1700489400000\n ]\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"config\": {\n \"displayNameFromDS\": \"d111\"\n },\n \"values\": [\n 11254,\n 24739,\n 25406,\n 21239,\n 19426\n ],\n \"labels\": {\n \"nodename\": \"q111\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"timeseries-multi\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n },\n {\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"config\": {},\n \"values\": [\n 1700488200000,\n 1700488500000,\n 1700488800000,\n 1700489100000,\n 1700489400000\n ]\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"config\": {\n \"displayNameFromDS\": \"d102\"\n },\n \"values\": [\n 168,\n 264,\n 288,\n 283,\n 267\n ],\n \"labels\": {\n \"nodename\": \"d102\"\n }\n }\n ],\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"timeseries-multi\",\n \"typeVersion\": [\n 0,\n 0\n ]\n }\n }\n]", "refId": "A", @@ -883,12 +869,7 @@ ], "refresh": "", "schemaVersion": 39, - "tags": [ - "gdev", - "panel-tests", - "barchart", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "barchart", "graph-ng"], "templating": { "list": [] }, @@ -902,4 +883,4 @@ "uid": "ea33320b-bd97-4fe1-a27c-24bc61a48b41", "version": 5, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json b/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json index eda6ccfe996..ebfdf855286 100644 --- a/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json +++ b/devenv/dev-dashboards/panel-bargauge/bar_gauge_demo.json @@ -19,7 +19,9 @@ "links": [], "panels": [ { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 7, "w": 24, @@ -149,7 +151,9 @@ "type": "bargauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 10, "w": 16, @@ -231,7 +235,9 @@ "type": "bargauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 10, "w": 6, @@ -328,7 +334,9 @@ "type": "bargauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 22, "w": 2, @@ -375,7 +383,9 @@ "type": "bargauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 12, "w": 22, @@ -509,7 +519,9 @@ "type": "bargauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 8, "w": 24, diff --git a/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json b/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json index cc8f600140f..d2f6271be2b 100644 --- a/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json +++ b/devenv/dev-dashboards/panel-bargauge/panel_tests_bar_gauge.json @@ -28,7 +28,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -79,9 +81,7 @@ "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -93,8 +93,7 @@ { "alias": "Inside", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "H", "scenarioId": "csv_metric_values", @@ -103,16 +102,14 @@ { "alias": "Outhouse", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "random_walk" @@ -122,7 +119,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -173,9 +172,7 @@ "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -187,8 +184,7 @@ { "alias": "Inside", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "H", "scenarioId": "csv_metric_values", @@ -197,32 +193,28 @@ { "alias": "Outhouse", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "random_walk" @@ -232,7 +224,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -283,9 +277,7 @@ "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -297,8 +289,7 @@ { "alias": "Inside", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "H", "scenarioId": "csv_metric_values", @@ -307,32 +298,28 @@ { "alias": "Outhouse", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "random_walk" @@ -342,7 +329,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -393,9 +382,7 @@ "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -407,8 +394,7 @@ { "alias": "Inside", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "H", "scenarioId": "csv_metric_values", @@ -417,32 +403,28 @@ { "alias": "Outhouse", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "random_walk" @@ -452,7 +434,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -503,9 +487,7 @@ "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -517,8 +499,7 @@ { "alias": "Inside", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "H", "scenarioId": "csv_metric_values", @@ -527,32 +508,28 @@ { "alias": "Outhouse", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "random_walk" @@ -562,7 +539,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -613,9 +592,7 @@ "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -627,8 +604,7 @@ { "alias": "Inside", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "H", "scenarioId": "csv_metric_values", @@ -637,32 +613,28 @@ { "alias": "Outhouse", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "random_walk" @@ -672,7 +644,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -718,9 +692,7 @@ "minVizWidth": 0, "orientation": "vertical", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -731,8 +703,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "E", "scenarioId": "csv_metric_values", @@ -740,24 +711,21 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "G", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "H", "scenarioId": "csv_metric_values", @@ -765,56 +733,49 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "I", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "J", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "K", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "L", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "M", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "N", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "O", "scenarioId": "random_walk" @@ -824,7 +785,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -870,9 +833,7 @@ "minVizWidth": 0, "orientation": "vertical", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -883,8 +844,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "H", "scenarioId": "csv_metric_values", @@ -892,104 +852,91 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "I", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "J", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "K", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "L", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "M", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "N", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "O", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "P", "scenarioId": "random_walk" }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "Q", "scenarioId": "random_walk" @@ -999,7 +946,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1044,9 +993,7 @@ "minVizWidth": 0, "orientation": "horizontal", "reduceOptions": { - "calcs": [ - "last" - ], + "calcs": ["last"], "fields": "", "values": false }, @@ -1057,8 +1004,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1069,7 +1015,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1114,9 +1062,7 @@ "minVizWidth": 0, "orientation": "vertical", "reduceOptions": { - "calcs": [ - "last" - ], + "calcs": ["last"], "fields": "", "values": false }, @@ -1127,8 +1073,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1139,7 +1084,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1184,9 +1131,7 @@ "minVizWidth": 0, "orientation": "vertical", "reduceOptions": { - "calcs": [ - "last" - ], + "calcs": ["last"], "fields": "", "values": false }, @@ -1197,8 +1142,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1209,7 +1153,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1259,9 +1205,7 @@ "minVizWidth": 0, "orientation": "vertical", "reduceOptions": { - "calcs": [ - "last" - ], + "calcs": ["last"], "fields": "", "values": false }, @@ -1272,8 +1216,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1284,7 +1227,9 @@ "type": "bargauge" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1334,9 +1279,7 @@ "minVizWidth": 0, "orientation": "vertical", "reduceOptions": { - "calcs": [ - "last" - ], + "calcs": ["last"], "fields": "", "values": false }, @@ -1347,8 +1290,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1361,10 +1303,7 @@ ], "revision": 1, "schemaVersion": 38, - "tags": [ - "gdev", - "panel-tests" - ], + "tags": ["gdev", "panel-tests"], "templating": { "list": [] }, @@ -1373,18 +1312,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - Bar Gauge", diff --git a/devenv/dev-dashboards/panel-candlestick/candlestick.json b/devenv/dev-dashboards/panel-candlestick/candlestick.json index 63a57ad394b..40c153d092c 100644 --- a/devenv/dev-dashboards/panel-candlestick/candlestick.json +++ b/devenv/dev-dashboards/panel-candlestick/candlestick.json @@ -29,7 +29,7 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -49,10 +49,7 @@ }, "lineInterpolation": "linear", "lineStyle": { - "dash": [ - 10, - 10 - ], + "dash": [10, 10], "fill": "dash" }, "lineWidth": 1, @@ -106,10 +103,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 0, - 20 - ], + "dash": [0, 20], "fill": "dot" } } @@ -174,7 +168,7 @@ "type": "candlestick" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -260,7 +254,7 @@ "type": "candlestick" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -346,7 +340,7 @@ "type": "candlestick" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -467,11 +461,7 @@ ], "refresh": false, "schemaVersion": 36, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-canvas/canvas-connection-examples.json b/devenv/dev-dashboards/panel-canvas/canvas-connection-examples.json index aedd6be604f..943ba719303 100644 --- a/devenv/dev-dashboards/panel-canvas/canvas-connection-examples.json +++ b/devenv/dev-dashboards/panel-canvas/canvas-connection-examples.json @@ -29,8 +29,7 @@ "panels": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2213,8 +2212,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -2225,7 +2223,9 @@ "type": "canvas" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2843,8 +2843,7 @@ { "csvContent": "gateway, product-details, product-reviews, reviews-ratings, details-checkout\n100, 56, 44, 22, 28", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -2854,7 +2853,9 @@ "type": "canvas" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -3609,8 +3610,7 @@ { "csvContent": "database_server, server_database, server_region, region_server, database2_server, server_database2\n10, 53, 35, 12, 22, 81", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -3623,11 +3623,7 @@ "refresh": "", "revision": 1, "schemaVersion": 39, - "tags": [ - "gdev", - "panel-tests", - "canvas" - ], + "tags": ["gdev", "panel-tests", "canvas"], "templating": { "list": [] }, @@ -3642,4 +3638,4 @@ "uid": "Pu8lwQAVz", "version": 6, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-canvas/canvas-datalinks.json b/devenv/dev-dashboards/panel-canvas/canvas-datalinks.json index 7ecbdd30838..5ec2bd179a6 100644 --- a/devenv/dev-dashboards/panel-canvas/canvas-datalinks.json +++ b/devenv/dev-dashboards/panel-canvas/canvas-datalinks.json @@ -1,3449 +1,2264 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "description": "", - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "links": [], - "panels": [ - { - "datasource": { - "type": "grafana", - "uid": "grafana" + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "datasource": { + "type": "grafana", + "uid": "grafana" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "A-hehwzd" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "A-hehwzd" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "A-series data link", - "url": "http://localhost:3000/d/dddouk2ygsb9cc/6114e28a-4041-5fbd-878f-b98718a90c4d?${__url_time_range}" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Count (transformation)" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "Count data link", - "url": "http://localhost:3000/d/dddouk2ygsb9cc/6114e28a-4041-5fbd-878f-b98718a90c4d${__data.fields.A-series}" - } - ] - } - ] - } + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "A-series data link", + "url": "http://localhost:3000/d/dddouk2ygsb9cc/6114e28a-4041-5fbd-878f-b98718a90c4d?${__url_time_range}" + } ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Count (transformation)" }, - "gridPos": { - "h": 30, - "w": 24, - "x": 0, - "y": 0 + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Count data link", + "url": "http://localhost:3000/d/dddouk2ygsb9cc/6114e28a-4041-5fbd-878f-b98718a90c4d${__data.fields.A-series}" + } + ] + } + ] + } + ] + }, + "gridPos": { + "h": 30, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "infinitePan": false, + "inlineEditing": false, + "panZoom": false, + "root": { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" }, - "id": 1, - "options": { - "infinitePan": false, - "inlineEditing": false, - "panZoom": false, - "root": { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - }, - "width": 0 - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "elements": [ - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 24, - "text": { - "fixed": "Metric Value" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 1", - "placement": { - "height": 50, - "left": 38, - "top": 23, - "width": 144 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "size": 20, - "text": { - "field": "A-hehwzd", - "fixed": "", - "mode": "field" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 2", - "placement": { - "height": 50, - "left": 16, - "top": 91, - "width": 188 - }, - "type": "metric-value" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "field": "A-hehwzd", - "fixed": "#000000" - }, - "size": 20, - "text": { - "fixed": "Text color", - "mode": "fixed" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 3", - "placement": { - "height": 50, - "left": 16, - "top": 170, - "width": 188 - }, - "type": "metric-value" - }, - { - "background": { - "color": { - "fixed": "transparent" - }, - "image": { - "field": "Count (transformation)", - "mode": "field" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "text" - }, - "size": 20, - "text": { - "fixed": "Background image", - "mode": "fixed" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 4", - "placement": { - "height": 50, - "left": 16, - "top": 249, - "width": 188 - }, - "type": "metric-value" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "field": "A-hehwzd", - "fixed": "dark-green" - }, - "width": 2 - }, - "config": { - "align": "center", - "color": { - "fixed": "text" - }, - "size": 20, - "text": { - "fixed": "Border", - "mode": "fixed" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 5", - "placement": { - "height": 50, - "left": 16, - "top": 328, - "width": 188 - }, - "type": "metric-value" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "text" - }, - "size": 16, - "text": { - "field": "A-hehwzd", - "mode": "field" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 6", - "placement": { - "height": 50, - "left": 262, - "top": 91, - "width": 188 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "transparent" - }, - "image": { - "fixed": "" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "field": "A-hehwzd", - "fixed": "rgb(204, 204, 220)" - }, - "size": 16, - "text": { - "fixed": "Text color" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 7", - "placement": { - "height": 50, - "left": 262, - "top": 170, - "width": 188 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "transparent" - }, - "image": { - "field": "Count (transformation)", - "mode": "field" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 16, - "text": { - "fixed": "Background image" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 8", - "placement": { - "height": 50, - "left": 262, - "top": 249, - "width": 188 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "field": "A-hehwzd", - "fixed": "dark-green" - }, - "width": 2 - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 16, - "text": { - "fixed": "Border" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 9", - "placement": { - "height": 50, - "left": 262, - "top": 328, - "width": 188 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 24, - "text": { - "fixed": "Text" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 10", - "placement": { - "height": 50, - "left": 284, - "top": 23, - "width": 144 - }, - "type": "text" - }, - { - "background": { - "fixed": "#D9D9D9" - }, - "border": { - "color": { - "fixed": "transparent" - }, - "width": 1 - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "field": "A-hehwzd", - "mode": "field" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 11", - "placement": { - "height": 50, - "left": 530, - "top": 91, - "width": 188 - }, - "type": "ellipse" - }, - { - "background": { - "fixed": "#D9D9D9" - }, - "border": { - "color": { - "fixed": "transparent" - }, - "width": 1 - }, - "config": { - "align": "center", - "color": { - "field": "A-hehwzd", - "fixed": "#000000" - }, - "text": { - "fixed": "Text color" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 12", - "placement": { - "height": 50, - "left": 530, - "top": 170, - "width": 188 - }, - "type": "ellipse" - }, - { - "background": { - "fixed": "#D9D9D9", - "image": { - "field": "Count (transformation)", - "fixed": "", - "mode": "field" - } - }, - "border": { - "color": { - "fixed": "transparent" - }, - "width": 1 - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "fixed": "Background image" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 13", - "placement": { - "height": 50, - "left": 530, - "top": 248.33333333333334, - "width": 187.99997965494794 - }, - "type": "ellipse" - }, - { - "background": { - "fixed": "#D9D9D9" - }, - "border": { - "color": { - "field": "A-hehwzd", - "fixed": "transparent" - }, - "radius": 0, - "width": 2 - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "fixed": "Border" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 14", - "placement": { - "height": 50, - "left": 530, - "top": 328.33333333333337, - "width": 187.99997965494794 - }, - "type": "ellipse" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 24, - "text": { - "fixed": "Ellipse" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 15", - "placement": { - "height": 50, - "left": 552, - "top": 23, - "width": 144 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "field": "A-hehwzd", - "mode": "field" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 16", - "placement": { - "height": 50, - "left": 798, - "top": 91, - "width": 188 - }, - "type": "rectangle" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "field": "A-hehwzd", - "fixed": "#000000" - }, - "text": { - "fixed": "Text color" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 17", - "placement": { - "height": 50, - "left": 798, - "top": 170, - "width": 188 - }, - "type": "rectangle" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - }, - "image": { - "field": "Count (transformation)", - "mode": "field" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "fixed": "Background image" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 18", - "placement": { - "height": 50, - "left": 798.3333333333334, - "top": 248.33333333333334, - "width": 187.99997965494794 - }, - "type": "rectangle" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "field": "A-hehwzd", - "fixed": "dark-green" - }, - "width": 2 - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "fixed": "Border" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 19", - "placement": { - "height": 50, - "left": 798.3333333333334, - "top": 328.33333333333337, - "width": 187.99235026041669 - }, - "type": "rectangle" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 24, - "text": { - "fixed": "Rectangle" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 20", - "placement": { - "height": 50, - "left": 820, - "top": 23, - "width": 144 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "fill": { - "fixed": "#D9D9D9" - }, - "path": { - "field": "A-hehwzd", - "fixed": "img/icons/unicons/question-circle.svg", - "mode": "field" - } - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 21", - "placement": { - "height": 50, - "left": 1066, - "top": 91, - "width": 188 - }, - "type": "icon" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "fill": { - "field": "A-hehwzd", - "fixed": "#D9D9D9" - }, - "path": { - "fixed": "img/icons/unicons/question-circle.svg", - "mode": "fixed" - } - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 22", - "placement": { - "height": 50, - "left": 1066, - "top": 170, - "width": 188 - }, - "type": "icon" - }, - { - "background": { - "color": { - "fixed": "transparent" - }, - "image": { - "field": "Count (transformation)", - "mode": "field" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "fill": { - "fixed": "#D9D9D9" - }, - "path": { - "fixed": "img/icons/unicons/question-circle.svg", - "mode": "fixed" - } - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 23", - "placement": { - "height": 50, - "left": 1066, - "top": 249, - "width": 188 - }, - "type": "icon" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "field": "A-hehwzd", - "fixed": "dark-green" - }, - "width": 2 - }, - "config": { - "fill": { - "fixed": "#D9D9D9" - }, - "path": { - "fixed": "img/icons/unicons/question-circle.svg", - "mode": "fixed" - } - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 24", - "placement": { - "height": 50, - "left": 1066, - "top": 328, - "width": 188 - }, - "type": "icon" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 24, - "text": { - "fixed": "Icon" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 25", - "placement": { - "height": 50, - "left": 1088, - "top": 23, - "width": 144 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "statusColor": { - "field": "A-hehwzd" - }, - "type": "Single" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 26", - "placement": { - "height": 90, - "left": 65, - "top": 516, - "width": 90 - }, - "type": "server" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "field": "A-hehwzd", - "fixed": "dark-green" - }, - "width": 2 - }, - "config": { - "type": "Single" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 29", - "placement": { - "height": 90, - "left": 65, - "top": 870, - "width": 90 - }, - "type": "server" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 24, - "text": { - "fixed": "Server" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 30", - "placement": { - "height": 50, - "left": 38, - "top": 438, - "width": 144 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "bulbColor": { - "field": "A-hehwzd" - }, - "statusColor": { - "fixed": "transparent" - }, - "type": "Single" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 31", - "placement": { - "height": 90, - "left": 65, - "top": 634, - "width": 90 - }, - "type": "server" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "blinkRate": { - "field": "A-hehwzd" - }, - "bulbColor": { - "fixed": "#000000" - }, - "statusColor": { - "fixed": "transparent" - }, - "type": "Single" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 32", - "placement": { - "height": 90, - "left": 65, - "top": 752, - "width": 90 - }, - "type": "server" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "rpm": { - "field": "A-hehwzd" - }, - "statusColor": { - "field": "A-hehwzd" - }, - "type": "Single" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 33", - "placement": { - "height": 55, - "left": 329, - "top": 516, - "width": 55 - }, - "type": "windTurbine" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 24, - "text": { - "fixed": "Wind Turbine" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 35", - "placement": { - "height": 50, - "left": 270, - "top": 438, - "width": 172 - }, - "type": "text" - }, - { - "background": { - "color": { - "field": "A-hehwzd", - "fixed": "transparent" - }, - "image": { - "field": "Count (transformation)", - "mode": "field" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "bulbColor": { - "field": "A-hehwzd" - }, - "statusColor": { - "fixed": "transparent" - }, - "type": "Single" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 36", - "placement": { - "height": 55, - "left": 329, - "top": 633, - "width": 55 - }, - "type": "windTurbine" - }, - { - "background": { - "color": { - "field": "Count (transformation)", - "fixed": "transparent" - }, - "image": { - "field": "A-hehwzd", - "mode": "field" - } - }, - "border": { - "color": { - "fixed": "dark-green" - }, - "width": 0 - }, - "config": { - "type": "Single" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 38", - "placement": { - "height": 90, - "left": 65, - "top": 988, - "width": 90 - }, - "type": "server" - }, - { - "background": { - "color": { - "fixed": "transparent" - }, - "image": { - "field": "", - "mode": "field" - } - }, - "border": { - "color": { - "field": "A-hehwzd", - "fixed": "dark-green" - }, - "width": 1 - }, - "config": { - "bulbColor": { - "field": "A-hehwzd" - }, - "statusColor": { - "fixed": "transparent" - }, - "type": "Single" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 37", - "placement": { - "height": 55, - "left": 329, - "top": 752, - "width": 55 - }, - "type": "windTurbine" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 24, - "text": { - "fixed": "Cloud" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 39", - "placement": { - "height": 50, - "left": 530, - "top": 438, - "width": 172 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "field": "A-hehwzd", - "mode": "field" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 40", - "placement": { - "height": 50, - "left": 530, - "top": 517, - "width": 188 - }, - "type": "cloud" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "field": "A-hehwzd", - "fixed": "#000000" - }, - "text": { - "fixed": "Text color" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 41", - "placement": { - "height": 50, - "left": 530, - "top": 596, - "width": 188 - }, - "type": "cloud" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - }, - "image": { - "field": "Count (transformation)", - "mode": "field" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "left", - "color": { - "fixed": "#000000" - }, - "text": { - "fixed": "Background image" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 42", - "placement": { - "height": 50, - "left": 530, - "top": 675, - "width": 188 - }, - "type": "cloud" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "field": "A-hehwzd", - "fixed": "dark-green" - }, - "width": 5 - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "fixed": "Border" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 43", - "placement": { - "height": 50, - "left": 530, - "top": 754, - "width": 188 - }, - "type": "cloud" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 24, - "text": { - "fixed": "Parallelogram" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 44", - "placement": { - "height": 50, - "left": 806, - "top": 438, - "width": 172 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "field": "A-hehwzd", - "fixed": "", - "mode": "field" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 45", - "placement": { - "height": 50, - "left": 798, - "top": 516, - "width": 188 - }, - "type": "parallelogram" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "field": "A-hehwzd", - "fixed": "#000000" - }, - "text": { - "fixed": "Text color" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 46", - "placement": { - "height": 50, - "left": 798, - "top": 595, - "width": 188 - }, - "type": "parallelogram" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - }, - "image": { - "field": "Count (transformation)", - "mode": "field" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "left", - "color": { - "fixed": "#000000" - }, - "text": { - "fixed": "Background image" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 47", - "placement": { - "height": 50, - "left": 798, - "top": 674, - "width": 188 - }, - "type": "parallelogram" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "field": "A-hehwzd", - "fixed": "dark-green" - }, - "width": 5 - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "fixed": "Border" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 48", - "placement": { - "height": 50, - "left": 798, - "top": 753, - "width": 188 - }, - "type": "parallelogram" - }, - { - "background": { - "color": { - "fixed": "transparent" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "rgb(204, 204, 220)" - }, - "size": 24, - "text": { - "fixed": "Triangle" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 49", - "placement": { - "height": 50, - "left": 1074, - "top": 438, - "width": 172 - }, - "type": "text" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "field": "A-hehwzd", - "fixed": "", - "mode": "field" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 50", - "placement": { - "height": 50, - "left": 1066, - "top": 516, - "width": 188 - }, - "type": "triangle" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "center", - "color": { - "field": "A-hehwzd", - "fixed": "#000000" - }, - "text": { - "fixed": "Text color" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 51", - "placement": { - "height": 50, - "left": 1066, - "top": 595, - "width": 188 - }, - "type": "triangle" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - }, - "image": { - "field": "Count (transformation)", - "mode": "field" - } - }, - "border": { - "color": { - "fixed": "dark-green" - } - }, - "config": { - "align": "left", - "color": { - "fixed": "#000000" - }, - "text": { - "fixed": "Background image" - }, - "valign": "bottom" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 52", - "placement": { - "height": 50, - "left": 1066, - "top": 674, - "width": 188 - }, - "type": "triangle" - }, - { - "background": { - "color": { - "fixed": "#D9D9D9" - } - }, - "border": { - "color": { - "field": "A-hehwzd", - "fixed": "dark-green" - }, - "width": 5 - }, - "config": { - "align": "center", - "color": { - "fixed": "#000000" - }, - "text": { - "fixed": "Border" - }, - "valign": "middle" - }, - "constraint": { - "horizontal": "left", - "vertical": "top" - }, - "name": "Element 53", - "placement": { - "height": 50, - "left": 1066, - "top": 753, - "width": 188 - }, - "type": "triangle" - } - ], - "name": "Element 1708700648848", - "placement": { - "height": 100, - "left": 0, - "rotation": 0, - "top": 0, - "width": 100 - }, - "type": "frame" + "width": 0 + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "elements": [ + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" }, - "showAdvancedTypes": true + "size": 24, + "text": { + "fixed": "Metric Value" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 1", + "placement": { + "height": 50, + "left": 38, + "top": 23, + "width": 144 + }, + "type": "text" }, - "pluginVersion": "11.1.0-pre", - "targets": [ - { - "datasource": { - "type": "grafana", - "uid": "grafana" + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "size": 20, + "text": { + "field": "A-hehwzd", + "fixed": "", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 2", + "placement": { + "height": 50, + "left": 16, + "top": 91, + "width": 188 + }, + "type": "metric-value" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "A-hehwzd", + "fixed": "#000000" + }, + "size": 20, + "text": { + "fixed": "Text color", + "mode": "fixed" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 3", + "placement": { + "height": 50, + "left": 16, + "top": 170, + "width": 188 + }, + "type": "metric-value" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "field": "Count (transformation)", + "mode": "field" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "text" + }, + "size": 20, + "text": { + "fixed": "Background image", + "mode": "fixed" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 4", + "placement": { + "height": 50, + "left": 16, + "top": 249, + "width": 188 + }, + "type": "metric-value" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "field": "A-hehwzd", + "fixed": "dark-green" + }, + "width": 2 + }, + "config": { + "align": "center", + "color": { + "fixed": "text" + }, + "size": 20, + "text": { + "fixed": "Border", + "mode": "fixed" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 5", + "placement": { + "height": 50, + "left": 16, + "top": 328, + "width": 188 + }, + "type": "metric-value" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "text" + }, + "size": 16, + "text": { + "field": "A-hehwzd", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 6", + "placement": { + "height": 50, + "left": 262, + "top": 91, + "width": 188 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "fixed": "" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "A-hehwzd", + "fixed": "rgb(204, 204, 220)" + }, + "size": 16, + "text": { + "fixed": "Text color" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 7", + "placement": { + "height": 50, + "left": 262, + "top": 170, + "width": 188 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "field": "Count (transformation)", + "mode": "field" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 16, + "text": { + "fixed": "Background image" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 8", + "placement": { + "height": 50, + "left": 262, + "top": 249, + "width": 188 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "field": "A-hehwzd", + "fixed": "dark-green" + }, + "width": 2 + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 16, + "text": { + "fixed": "Border" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 9", + "placement": { + "height": 50, + "left": 262, + "top": 328, + "width": 188 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 24, + "text": { + "fixed": "Text" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 10", + "placement": { + "height": 50, + "left": 284, + "top": 23, + "width": 144 + }, + "type": "text" + }, + { + "background": { + "fixed": "#D9D9D9" + }, + "border": { + "color": { + "fixed": "transparent" + }, + "width": 1 + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "field": "A-hehwzd", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 11", + "placement": { + "height": 50, + "left": 530, + "top": 91, + "width": 188 + }, + "type": "ellipse" + }, + { + "background": { + "fixed": "#D9D9D9" + }, + "border": { + "color": { + "fixed": "transparent" + }, + "width": 1 + }, + "config": { + "align": "center", + "color": { + "field": "A-hehwzd", + "fixed": "#000000" + }, + "text": { + "fixed": "Text color" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 12", + "placement": { + "height": 50, + "left": 530, + "top": 170, + "width": 188 + }, + "type": "ellipse" + }, + { + "background": { + "fixed": "#D9D9D9", + "image": { + "field": "Count (transformation)", + "fixed": "", + "mode": "field" + } + }, + "border": { + "color": { + "fixed": "transparent" + }, + "width": 1 + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "Background image" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 13", + "placement": { + "height": 50, + "left": 530, + "top": 248.33333333333334, + "width": 187.99997965494794 + }, + "type": "ellipse" + }, + { + "background": { + "fixed": "#D9D9D9" + }, + "border": { + "color": { + "field": "A-hehwzd", + "fixed": "transparent" + }, + "radius": 0, + "width": 2 + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "Border" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 14", + "placement": { + "height": 50, + "left": 530, + "top": 328.33333333333337, + "width": 187.99997965494794 + }, + "type": "ellipse" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 24, + "text": { + "fixed": "Ellipse" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 15", + "placement": { + "height": 50, + "left": 552, + "top": 23, + "width": 144 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "field": "A-hehwzd", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 16", + "placement": { + "height": 50, + "left": 798, + "top": 91, + "width": 188 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "A-hehwzd", + "fixed": "#000000" + }, + "text": { + "fixed": "Text color" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 17", + "placement": { + "height": 50, + "left": 798, + "top": 170, + "width": 188 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + }, + "image": { + "field": "Count (transformation)", + "mode": "field" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "Background image" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 18", + "placement": { + "height": 50, + "left": 798.3333333333334, + "top": 248.33333333333334, + "width": 187.99997965494794 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "field": "A-hehwzd", + "fixed": "dark-green" + }, + "width": 2 + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "Border" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 19", + "placement": { + "height": 50, + "left": 798.3333333333334, + "top": 328.33333333333337, + "width": 187.99235026041669 + }, + "type": "rectangle" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 24, + "text": { + "fixed": "Rectangle" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 20", + "placement": { + "height": 50, + "left": 820, + "top": 23, + "width": 144 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "fill": { + "fixed": "#D9D9D9" + }, + "path": { + "field": "A-hehwzd", + "fixed": "img/icons/unicons/question-circle.svg", + "mode": "field" + } + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 21", + "placement": { + "height": 50, + "left": 1066, + "top": 91, + "width": 188 + }, + "type": "icon" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "fill": { + "field": "A-hehwzd", + "fixed": "#D9D9D9" + }, + "path": { + "fixed": "img/icons/unicons/question-circle.svg", + "mode": "fixed" + } + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 22", + "placement": { + "height": 50, + "left": 1066, + "top": 170, + "width": 188 + }, + "type": "icon" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "field": "Count (transformation)", + "mode": "field" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "fill": { + "fixed": "#D9D9D9" + }, + "path": { + "fixed": "img/icons/unicons/question-circle.svg", + "mode": "fixed" + } + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 23", + "placement": { + "height": 50, + "left": 1066, + "top": 249, + "width": 188 + }, + "type": "icon" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "field": "A-hehwzd", + "fixed": "dark-green" + }, + "width": 2 + }, + "config": { + "fill": { + "fixed": "#D9D9D9" + }, + "path": { + "fixed": "img/icons/unicons/question-circle.svg", + "mode": "fixed" + } + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 24", + "placement": { + "height": 50, + "left": 1066, + "top": 328, + "width": 188 + }, + "type": "icon" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 24, + "text": { + "fixed": "Icon" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 25", + "placement": { + "height": 50, + "left": 1088, + "top": 23, + "width": 144 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "statusColor": { + "field": "A-hehwzd" + }, + "type": "Single" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 26", + "placement": { + "height": 90, + "left": 65, + "top": 516, + "width": 90 + }, + "type": "server" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "field": "A-hehwzd", + "fixed": "dark-green" + }, + "width": 2 + }, + "config": { + "type": "Single" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 29", + "placement": { + "height": 90, + "left": 65, + "top": 870, + "width": 90 + }, + "type": "server" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 24, + "text": { + "fixed": "Server" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 30", + "placement": { + "height": 50, + "left": 38, + "top": 438, + "width": 144 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "bulbColor": { + "field": "A-hehwzd" + }, + "statusColor": { + "fixed": "transparent" + }, + "type": "Single" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 31", + "placement": { + "height": 90, + "left": 65, + "top": 634, + "width": 90 + }, + "type": "server" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "blinkRate": { + "field": "A-hehwzd" + }, + "bulbColor": { + "fixed": "#000000" + }, + "statusColor": { + "fixed": "transparent" + }, + "type": "Single" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 32", + "placement": { + "height": 90, + "left": 65, + "top": 752, + "width": 90 + }, + "type": "server" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "rpm": { + "field": "A-hehwzd" + }, + "statusColor": { + "field": "A-hehwzd" + }, + "type": "Single" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 33", + "placement": { + "height": 55, + "left": 329, + "top": 516, + "width": 55 + }, + "type": "windTurbine" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 24, + "text": { + "fixed": "Wind Turbine" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 35", + "placement": { + "height": 50, + "left": 270, + "top": 438, + "width": 172 + }, + "type": "text" + }, + { + "background": { + "color": { + "field": "A-hehwzd", + "fixed": "transparent" + }, + "image": { + "field": "Count (transformation)", + "mode": "field" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "bulbColor": { + "field": "A-hehwzd" + }, + "statusColor": { + "fixed": "transparent" + }, + "type": "Single" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 36", + "placement": { + "height": 55, + "left": 329, + "top": 633, + "width": 55 + }, + "type": "windTurbine" + }, + { + "background": { + "color": { + "field": "Count (transformation)", + "fixed": "transparent" + }, + "image": { + "field": "A-hehwzd", + "mode": "field" + } + }, + "border": { + "color": { + "fixed": "dark-green" + }, + "width": 0 + }, + "config": { + "type": "Single" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 38", + "placement": { + "height": 90, + "left": 65, + "top": 988, + "width": 90 + }, + "type": "server" + }, + { + "background": { + "color": { + "fixed": "transparent" + }, + "image": { + "field": "", + "mode": "field" + } + }, + "border": { + "color": { + "field": "A-hehwzd", + "fixed": "dark-green" + }, + "width": 1 + }, + "config": { + "bulbColor": { + "field": "A-hehwzd" + }, + "statusColor": { + "fixed": "transparent" + }, + "type": "Single" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 37", + "placement": { + "height": 55, + "left": 329, + "top": 752, + "width": 55 + }, + "type": "windTurbine" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 24, + "text": { + "fixed": "Cloud" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 39", + "placement": { + "height": 50, + "left": 530, + "top": 438, + "width": 172 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "field": "A-hehwzd", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 40", + "placement": { + "height": 50, + "left": 530, + "top": 517, + "width": 188 + }, + "type": "cloud" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "A-hehwzd", + "fixed": "#000000" + }, + "text": { + "fixed": "Text color" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 41", + "placement": { + "height": 50, + "left": 530, + "top": 596, + "width": 188 + }, + "type": "cloud" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + }, + "image": { + "field": "Count (transformation)", + "mode": "field" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "left", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "Background image" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 42", + "placement": { + "height": 50, + "left": 530, + "top": 675, + "width": 188 + }, + "type": "cloud" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "field": "A-hehwzd", + "fixed": "dark-green" + }, + "width": 5 + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "Border" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 43", + "placement": { + "height": 50, + "left": 530, + "top": 754, + "width": 188 + }, + "type": "cloud" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 24, + "text": { + "fixed": "Parallelogram" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 44", + "placement": { + "height": 50, + "left": 806, + "top": 438, + "width": 172 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "field": "A-hehwzd", + "fixed": "", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 45", + "placement": { + "height": 50, + "left": 798, + "top": 516, + "width": 188 + }, + "type": "parallelogram" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "A-hehwzd", + "fixed": "#000000" + }, + "text": { + "fixed": "Text color" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 46", + "placement": { + "height": 50, + "left": 798, + "top": 595, + "width": 188 + }, + "type": "parallelogram" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + }, + "image": { + "field": "Count (transformation)", + "mode": "field" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "left", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "Background image" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 47", + "placement": { + "height": 50, + "left": 798, + "top": 674, + "width": 188 + }, + "type": "parallelogram" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "field": "A-hehwzd", + "fixed": "dark-green" + }, + "width": 5 + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "Border" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 48", + "placement": { + "height": 50, + "left": 798, + "top": 753, + "width": 188 + }, + "type": "parallelogram" + }, + { + "background": { + "color": { + "fixed": "transparent" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "rgb(204, 204, 220)" + }, + "size": 24, + "text": { + "fixed": "Triangle" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 49", + "placement": { + "height": 50, + "left": 1074, + "top": 438, + "width": 172 + }, + "type": "text" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "field": "A-hehwzd", + "fixed": "", + "mode": "field" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 50", + "placement": { + "height": 50, + "left": 1066, + "top": 516, + "width": 188 + }, + "type": "triangle" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "center", + "color": { + "field": "A-hehwzd", + "fixed": "#000000" + }, + "text": { + "fixed": "Text color" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 51", + "placement": { + "height": 50, + "left": 1066, + "top": 595, + "width": 188 + }, + "type": "triangle" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + }, + "image": { + "field": "Count (transformation)", + "mode": "field" + } + }, + "border": { + "color": { + "fixed": "dark-green" + } + }, + "config": { + "align": "left", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "Background image" + }, + "valign": "bottom" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 52", + "placement": { + "height": 50, + "left": 1066, + "top": 674, + "width": 188 + }, + "type": "triangle" + }, + { + "background": { + "color": { + "fixed": "#D9D9D9" + } + }, + "border": { + "color": { + "field": "A-hehwzd", + "fixed": "dark-green" + }, + "width": 5 + }, + "config": { + "align": "center", + "color": { + "fixed": "#000000" + }, + "text": { + "fixed": "Border" + }, + "valign": "middle" + }, + "constraint": { + "horizontal": "left", + "vertical": "top" + }, + "name": "Element 53", + "placement": { + "height": 50, + "left": 1066, + "top": 753, + "width": 188 + }, + "type": "triangle" + } + ], + "name": "Element 1708700648848", + "placement": { + "height": 100, + "left": 0, + "rotation": 0, + "top": 0, + "width": 100 + }, + "type": "frame" + }, + "showAdvancedTypes": true + }, + "pluginVersion": "11.1.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana", + "uid": "grafana" + }, + "queryType": "snapshot", + "refId": "A", + "snapshot": [ + { + "data": { + "values": [ + [ + 1708679336805, 1708679366805, 1708679396805, 1708679426805, 1708679456805, 1708679486805, + 1708679516805, 1708679546805, 1708679576805, 1708679606805, 1708679636805, 1708679666805, + 1708679696805, 1708679726805, 1708679756805, 1708679786805, 1708679816805, 1708679846805, + 1708679876805, 1708679906805, 1708679936805, 1708679966805, 1708679996805, 1708680026805, + 1708680056805, 1708680086805, 1708680116805, 1708680146805, 1708680176805, 1708680206805, + 1708680236805, 1708680266805, 1708680296805, 1708680326805, 1708680356805, 1708680386805, + 1708680416805, 1708680446805, 1708680476805, 1708680506805, 1708680536805, 1708680566805, + 1708680596805, 1708680626805, 1708680656805, 1708680686805, 1708680716805, 1708680746805, + 1708680776805, 1708680806805, 1708680836805, 1708680866805, 1708680896805, 1708680926805, + 1708680956805, 1708680986805, 1708681016805, 1708681046805, 1708681076805, 1708681106805, + 1708681136805, 1708681166805, 1708681196805, 1708681226805, 1708681256805, 1708681286805, + 1708681316805, 1708681346805, 1708681376805, 1708681406805, 1708681436805, 1708681466805, + 1708681496805, 1708681526805, 1708681556805, 1708681586805, 1708681616805, 1708681646805, + 1708681676805, 1708681706805, 1708681736805, 1708681766805, 1708681796805, 1708681826805, + 1708681856805, 1708681886805, 1708681916805, 1708681946805, 1708681976805, 1708682006805, + 1708682036805, 1708682066805, 1708682096805, 1708682126805, 1708682156805, 1708682186805, + 1708682216805, 1708682246805, 1708682276805, 1708682306805, 1708682336805, 1708682366805, + 1708682396805, 1708682426805, 1708682456805, 1708682486805, 1708682516805, 1708682546805, + 1708682576805, 1708682606805, 1708682636805, 1708682666805, 1708682696805, 1708682726805, + 1708682756805, 1708682786805, 1708682816805, 1708682846805, 1708682876805, 1708682906805, + 1708682936805, 1708682966805, 1708682996805, 1708683026805, 1708683056805, 1708683086805, + 1708683116805, 1708683146805, 1708683176805, 1708683206805, 1708683236805, 1708683266805, + 1708683296805, 1708683326805, 1708683356805, 1708683386805, 1708683416805, 1708683446805, + 1708683476805, 1708683506805, 1708683536805, 1708683566805, 1708683596805, 1708683626805, + 1708683656805, 1708683686805, 1708683716805, 1708683746805, 1708683776805, 1708683806805, + 1708683836805, 1708683866805, 1708683896805, 1708683926805, 1708683956805, 1708683986805, + 1708684016805, 1708684046805, 1708684076805, 1708684106805, 1708684136805, 1708684166805, + 1708684196805, 1708684226805, 1708684256805, 1708684286805, 1708684316805, 1708684346805, + 1708684376805, 1708684406805, 1708684436805, 1708684466805, 1708684496805, 1708684526805, + 1708684556805, 1708684586805, 1708684616805, 1708684646805, 1708684676805, 1708684706805, + 1708684736805, 1708684766805, 1708684796805, 1708684826805, 1708684856805, 1708684886805, + 1708684916805, 1708684946805, 1708684976805, 1708685006805, 1708685036805, 1708685066805, + 1708685096805, 1708685126805, 1708685156805, 1708685186805, 1708685216805, 1708685246805, + 1708685276805, 1708685306805, 1708685336805, 1708685366805, 1708685396805, 1708685426805, + 1708685456805, 1708685486805, 1708685516805, 1708685546805, 1708685576805, 1708685606805, + 1708685636805, 1708685666805, 1708685696805, 1708685726805, 1708685756805, 1708685786805, + 1708685816805, 1708685846805, 1708685876805, 1708685906805, 1708685936805, 1708685966805, + 1708685996805, 1708686026805, 1708686056805, 1708686086805, 1708686116805, 1708686146805, + 1708686176805, 1708686206805, 1708686236805, 1708686266805, 1708686296805, 1708686326805, + 1708686356805, 1708686386805, 1708686416805, 1708686446805, 1708686476805, 1708686506805, + 1708686536805, 1708686566805, 1708686596805, 1708686626805, 1708686656805, 1708686686805, + 1708686716805, 1708686746805, 1708686776805, 1708686806805, 1708686836805, 1708686866805, + 1708686896805, 1708686926805, 1708686956805, 1708686986805, 1708687016805, 1708687046805, + 1708687076805, 1708687106805, 1708687136805, 1708687166805, 1708687196805, 1708687226805, + 1708687256805, 1708687286805, 1708687316805, 1708687346805, 1708687376805, 1708687406805, + 1708687436805, 1708687466805, 1708687496805, 1708687526805, 1708687556805, 1708687586805, + 1708687616805, 1708687646805, 1708687676805, 1708687706805, 1708687736805, 1708687766805, + 1708687796805, 1708687826805, 1708687856805, 1708687886805, 1708687916805, 1708687946805, + 1708687976805, 1708688006805, 1708688036805, 1708688066805, 1708688096805, 1708688126805, + 1708688156805, 1708688186805, 1708688216805, 1708688246805, 1708688276805, 1708688306805, + 1708688336805, 1708688366805, 1708688396805, 1708688426805, 1708688456805, 1708688486805, + 1708688516805, 1708688546805, 1708688576805, 1708688606805, 1708688636805, 1708688666805, + 1708688696805, 1708688726805, 1708688756805, 1708688786805, 1708688816805, 1708688846805, + 1708688876805, 1708688906805, 1708688936805, 1708688966805, 1708688996805, 1708689026805, + 1708689056805, 1708689086805, 1708689116805, 1708689146805, 1708689176805, 1708689206805, + 1708689236805, 1708689266805, 1708689296805, 1708689326805, 1708689356805, 1708689386805, + 1708689416805, 1708689446805, 1708689476805, 1708689506805, 1708689536805, 1708689566805, + 1708689596805, 1708689626805, 1708689656805, 1708689686805, 1708689716805, 1708689746805, + 1708689776805, 1708689806805, 1708689836805, 1708689866805, 1708689896805, 1708689926805, + 1708689956805, 1708689986805, 1708690016805, 1708690046805, 1708690076805, 1708690106805, + 1708690136805, 1708690166805, 1708690196805, 1708690226805, 1708690256805, 1708690286805, + 1708690316805, 1708690346805, 1708690376805, 1708690406805, 1708690436805, 1708690466805, + 1708690496805, 1708690526805, 1708690556805, 1708690586805, 1708690616805, 1708690646805, + 1708690676805, 1708690706805, 1708690736805, 1708690766805, 1708690796805, 1708690826805, + 1708690856805, 1708690886805, 1708690916805, 1708690946805, 1708690976805, 1708691006805, + 1708691036805, 1708691066805, 1708691096805, 1708691126805, 1708691156805, 1708691186805, + 1708691216805, 1708691246805, 1708691276805, 1708691306805, 1708691336805, 1708691366805, + 1708691396805, 1708691426805, 1708691456805, 1708691486805, 1708691516805, 1708691546805, + 1708691576805, 1708691606805, 1708691636805, 1708691666805, 1708691696805, 1708691726805, + 1708691756805, 1708691786805, 1708691816805, 1708691846805, 1708691876805, 1708691906805, + 1708691936805, 1708691966805, 1708691996805, 1708692026805, 1708692056805, 1708692086805, + 1708692116805, 1708692146805, 1708692176805, 1708692206805, 1708692236805, 1708692266805, + 1708692296805, 1708692326805, 1708692356805, 1708692386805, 1708692416805, 1708692446805, + 1708692476805, 1708692506805, 1708692536805, 1708692566805, 1708692596805, 1708692626805, + 1708692656805, 1708692686805, 1708692716805, 1708692746805, 1708692776805, 1708692806805, + 1708692836805, 1708692866805, 1708692896805, 1708692926805, 1708692956805, 1708692986805, + 1708693016805, 1708693046805, 1708693076805, 1708693106805, 1708693136805, 1708693166805, + 1708693196805, 1708693226805, 1708693256805, 1708693286805, 1708693316805, 1708693346805, + 1708693376805, 1708693406805, 1708693436805, 1708693466805, 1708693496805, 1708693526805, + 1708693556805, 1708693586805, 1708693616805, 1708693646805, 1708693676805, 1708693706805, + 1708693736805, 1708693766805, 1708693796805, 1708693826805, 1708693856805, 1708693886805, + 1708693916805, 1708693946805, 1708693976805, 1708694006805, 1708694036805, 1708694066805, + 1708694096805, 1708694126805, 1708694156805, 1708694186805, 1708694216805, 1708694246805, + 1708694276805, 1708694306805, 1708694336805, 1708694366805, 1708694396805, 1708694426805, + 1708694456805, 1708694486805, 1708694516805, 1708694546805, 1708694576805, 1708694606805, + 1708694636805, 1708694666805, 1708694696805, 1708694726805, 1708694756805, 1708694786805, + 1708694816805, 1708694846805, 1708694876805, 1708694906805, 1708694936805, 1708694966805, + 1708694996805, 1708695026805, 1708695056805, 1708695086805, 1708695116805, 1708695146805, + 1708695176805, 1708695206805, 1708695236805, 1708695266805, 1708695296805, 1708695326805, + 1708695356805, 1708695386805, 1708695416805, 1708695446805, 1708695476805, 1708695506805, + 1708695536805, 1708695566805, 1708695596805, 1708695626805, 1708695656805, 1708695686805, + 1708695716805, 1708695746805, 1708695776805, 1708695806805, 1708695836805, 1708695866805, + 1708695896805, 1708695926805, 1708695956805, 1708695986805, 1708696016805, 1708696046805, + 1708696076805, 1708696106805, 1708696136805, 1708696166805, 1708696196805, 1708696226805, + 1708696256805, 1708696286805, 1708696316805, 1708696346805, 1708696376805, 1708696406805, + 1708696436805, 1708696466805, 1708696496805, 1708696526805, 1708696556805, 1708696586805, + 1708696616805, 1708696646805, 1708696676805, 1708696706805, 1708696736805, 1708696766805, + 1708696796805, 1708696826805, 1708696856805, 1708696886805, 1708696916805, 1708696946805, + 1708696976805, 1708697006805, 1708697036805, 1708697066805, 1708697096805, 1708697126805, + 1708697156805, 1708697186805, 1708697216805, 1708697246805, 1708697276805, 1708697306805, + 1708697336805, 1708697366805, 1708697396805, 1708697426805, 1708697456805, 1708697486805, + 1708697516805, 1708697546805, 1708697576805, 1708697606805, 1708697636805, 1708697666805, + 1708697696805, 1708697726805, 1708697756805, 1708697786805, 1708697816805, 1708697846805, + 1708697876805, 1708697906805, 1708697936805, 1708697966805, 1708697996805, 1708698026805, + 1708698056805, 1708698086805, 1708698116805, 1708698146805, 1708698176805, 1708698206805, + 1708698236805, 1708698266805, 1708698296805, 1708698326805, 1708698356805, 1708698386805, + 1708698416805, 1708698446805, 1708698476805, 1708698506805, 1708698536805, 1708698566805, + 1708698596805, 1708698626805, 1708698656805, 1708698686805, 1708698716805, 1708698746805, + 1708698776805, 1708698806805, 1708698836805, 1708698866805, 1708698896805, 1708698926805, + 1708698956805, 1708698986805, 1708699016805, 1708699046805, 1708699076805, 1708699106805, + 1708699136805, 1708699166805, 1708699196805, 1708699226805, 1708699256805, 1708699286805, + 1708699316805, 1708699346805, 1708699376805, 1708699406805, 1708699436805, 1708699466805, + 1708699496805, 1708699526805, 1708699556805, 1708699586805, 1708699616805, 1708699646805, + 1708699676805, 1708699706805, 1708699736805, 1708699766805, 1708699796805, 1708699826805, + 1708699856805, 1708699886805, 1708699916805, 1708699946805, 1708699976805, 1708700006805, + 1708700036805, 1708700066805, 1708700096805, 1708700126805, 1708700156805, 1708700186805, + 1708700216805, 1708700246805, 1708700276805, 1708700306805, 1708700336805, 1708700366805, + 1708700396805, 1708700426805, 1708700456805, 1708700486805, 1708700516805, 1708700546805, + 1708700576805, 1708700606805, 1708700636805, 1708700666805, 1708700696805, 1708700726805, + 1708700756805, 1708700786805, 1708700816805, 1708700846805, 1708700876805, 1708700906805 + ], + [ + 61.7880220958275, 61.81270791445456, 61.829208768453746, 61.3952804521061, 61.54378204663586, + 61.793292376739025, 61.89508181687579, 62.032346031223305, 62.090954811114734, 61.788446359641156, + 61.34681406228381, 61.37147422434034, 61.40948125782848, 61.69144945060843, 61.790001610516114, + 61.9726511399688, 62.17186395369245, 61.841593279449114, 61.81351577982158, 61.774548608665306, + 61.45071912098117, 61.837381696515884, 61.65059114296433, 61.1939165193637, 61.251406089255795, + 60.90456801094042, 60.56801269725712, 60.79736902333104, 60.9367183948152, 61.40785835680261, + 61.621326272755965, 61.53478423246078, 61.616180375313114, 61.56950269911591, 61.419724817297585, + 61.76631225002593, 62.12897242834343, 62.123394668856335, 61.84384351878861, 61.784342739352965, + 61.946359707479985, 62.00854561243485, 61.949378148238175, 61.83251536870273, 62.10046102360543, + 62.51955528279164, 62.43194543555232, 61.93563724102479, 61.61222776112464, 61.706572968014584, + 61.25653135192282, 61.356339636867965, 61.699918128409436, 61.700832273918536, 61.48680261436916, + 61.23885493750151, 60.88064816100224, 60.8061115971784, 60.48689168723215, 60.55420316390225, + 60.78326551379002, 60.71763616746389, 61.0384027799684, 61.361681117105356, 61.819694140841825, + 61.60303629180282, 61.13531779096821, 61.59247554276454, 62.073700396192514, 62.257393021269124, + 62.433793965222456, 62.250955190991135, 61.85027922321473, 61.991414140632, 62.103475488499456, + 62.1675085033716, 61.81759701584563, 61.44204189561965, 61.39004247589699, 61.84841370788845, + 62.01982884908544, 62.29261477385842, 62.1881407732118, 61.77974858108549, 61.30269030921595, + 61.72885233653658, 62.160061928899516, 62.13127060574461, 61.99860014382346, 62.2713524353439, + 62.45360209624644, 62.45033536766769, 62.74579940152534, 62.652379007396135, 62.50461375528075, + 62.4923739782267, 62.87608071230181, 62.979684959589086, 63.376138214369306, 63.66354651661235, + 63.77873383844567, 64.02365055931571, 63.57333739418103, 63.569251641994995, 63.51940188659398, + 63.81887126104792, 64.10808860762592, 64.26214829221084, 63.81353904099011, 63.792123977174654, + 64.09459044217124, 64.49651926575704, 64.87380353415439, 65.22102686208456, 65.49078940759729, + 65.83529933530703, 66.06313506256244, 66.36902912666964, 66.78048161957578, 66.85326316970041, + 67.14736719324404, 67.55927886414139, 67.497025942169, 67.84593513490577, 67.47441764273138, + 67.94871645670011, 68.32729017349827, 68.0857872436968, 67.93500201308075, 67.75781760651351, + 67.62321694227796, 67.87687655718156, 68.03974600191707, 67.82627187210018, 68.29863099170737, + 67.95842239244082, 67.56173819267319, 67.11089985276264, 67.58721546400895, 67.43817707268052, + 67.63734474285276, 67.8608900819446, 68.2491098921388, 68.64222598287019, 68.93436090180123, + 68.58516897681044, 68.88556663186037, 68.82509258379442, 68.43122435581691, 68.01151775860062, + 68.31539501569645, 68.01224489043588, 68.21383479637454, 67.85864255481124, 67.85285437130011, + 67.68770174333852, 67.78834746198736, 68.07096889676643, 68.06340992932907, 68.37116739265392, + 68.25606363201948, 68.0308569092854, 68.0279418148109, 67.68837311892736, 67.98015788941186, + 68.39001231797856, 68.55810426314746, 68.67055538403375, 68.24669275435873, 68.3542775530211, + 67.99792233705742, 67.93848132742042, 67.54327434886798, 67.86541640645122, 67.63715332453567, + 67.44863373545599, 67.54029786855584, 67.3799227903252, 67.272800723832, 67.65752571426705, + 67.27217330871126, 67.36259068699385, 67.6920166495926, 67.99237921892899, 68.00526928940994, + 67.67665255949264, 67.82106088034847, 68.22225474161398, 68.17103508007338, 68.1016199190692, + 67.9715626706774, 68.32706440829391, 67.87634330533211, 67.97207720874553, 67.87493382264455, + 67.38357737752105, 66.94768029712087, 66.67493510394992, 66.48367657533245, 66.77035937087237, + 66.87685661575208, 66.68590889792685, 67.11545823298368, 67.39896920212514, 67.34979904565203, + 67.19769898467558, 67.45512207826779, 67.07906680752531, 67.41541723819202, 67.15472798048341, + 67.15672773264464, 67.6290223056695, 68.11240707910342, 68.40594352628086, 67.92798893549167, + 67.46919856375305, 67.4835115803936, 67.63236807133978, 67.17328415938867, 67.24458272746313, + 66.84593455247185, 67.01430262963748, 67.05291762336604, 67.23817875420809, 66.98941509949245, + 66.68717338595964, 67.12826784930103, 66.74586515933792, 67.12397362624425, 66.91465424208462, + 66.68655917540693, 66.19881496899525, 66.68817057925634, 66.37134623059349, 66.596971028669, + 67.08641642499875, 67.12758650919066, 67.38518121414808, 67.46997153732326, 67.33145553844577, + 67.65363406004056, 67.45595043797628, 67.24978988019829, 67.39677514651268, 67.5978323405599, + 67.96140308646021, 68.00477049936622, 67.75104436464927, 67.99988077010511, 68.19366044789288, + 68.49848829432483, 68.25947149341026, 68.6317723773926, 68.91668608614779, 68.8159923502771, + 69.17513974647629, 69.23356802652235, 69.25309375298245, 69.21125949874141, 69.20726323550102, + 68.78019883289588, 69.24667107728621, 69.1153426259508, 69.56206231608063, 69.72141422867662, + 69.541238907092, 69.81884141217625, 69.53432369593423, 69.43315031151812, 69.01688148574168, + 68.90923934889648, 69.13664462290991, 69.31984528071725, 69.14875943039056, 69.48885652654923, + 69.88297168977773, 69.42462273878934, 69.12679413148601, 68.98123233159377, 68.7762758442149, + 68.48846507574687, 68.02689832504595, 68.02852643078998, 67.555096330903, 67.4639788904284, + 67.61277291577984, 67.70121565906206, 67.66817331556194, 67.84767253849441, 68.18538578924706, + 68.29860737971661, 68.53693780239855, 68.59722904625475, 68.72061222893264, 68.6709989466837, + 69.09400533816238, 69.20295511428635, 69.04258759148102, 68.77139467473914, 68.57756213054044, + 68.67449080482191, 68.60860533532343, 69.01676017621124, 69.3052161196497, 68.89017381442457, + 68.87423335356772, 68.94718162265079, 69.07199142609447, 68.79769510366016, 68.65106890757201, + 68.77703094552813, 68.28258034480257, 68.39479364216955, 68.34220719427985, 68.64253416645396, + 68.87725555362057, 68.5131252571485, 68.89127035251008, 68.67886068136877, 68.4447624537287, + 68.00181105736998, 68.16998112143412, 68.43591872875314, 68.02705208136817, 68.45211203302853, + 68.53289570629535, 68.58200283313724, 68.25718933019785, 68.36370956465593, 68.05020228727913, + 68.50658655636543, 68.66449948753682, 68.2241732330651, 68.09791466418139, 67.88940531760399, + 68.36978817923638, 67.96433264274425, 68.36349840881765, 68.48435193949715, 68.79844380614031, + 69.02017361165895, 68.52624119834393, 68.74005315812674, 68.76067653657759, 68.27918960695276, + 68.65714329915559, 68.42872809873309, 67.98192094490678, 68.02723949195821, 68.36423058076824, + 68.71132274325691, 68.6730162618873, 68.2039400216969, 67.86554691576616, 67.49988053949725, + 67.16307215146469, 67.0353669125758, 67.25486895820083, 66.95629780399439, 67.11477592840751, + 66.64323746050167, 66.20269172026232, 66.48026858749327, 66.4824015740555, 66.09931551662274, + 66.52915019033718, 66.45576884782787, 66.24667785942938, 65.81418399593981, 65.5611498094969, + 65.3398959293035, 65.77646026507297, 65.39346792956597, 65.41692492067247, 65.80888038895401, + 66.01134945900266, 65.80681809031556, 65.32295296805074, 65.37373854104783, 64.93533382859144, + 64.7705026812803, 65.17813748381224, 65.361732144628, 64.9021692008158, 64.7544901225187, + 64.89471070898715, 64.74868721773261, 65.00087272748908, 65.13124433830268, 64.86532668113782, + 64.51108629312537, 64.48208116580882, 64.86340417937541, 64.5927354669956, 64.1183990008181, + 63.66603997870095, 63.670535114339415, 63.179312479687646, 62.71414451358941, 62.349677869382184, + 62.73321111245177, 62.63668992149067, 62.246656820357686, 62.12151749467506, 62.09114940207692, + 62.13652644719604, 62.59170540130276, 62.28435878005047, 62.62555470862962, 62.73559453227453, + 62.63492310358647, 62.99493118502586, 63.2621260239913, 62.99279716454684, 62.8629723908099, + 63.01324605242161, 63.27456767760901, 63.11585082867133, 63.04279509050621, 63.10999097814922, + 63.544265738869036, 63.11822935137409, 63.01523903936018, 63.20403674621687, 62.83896086980938, + 62.41843196460615, 62.11985404869429, 62.07986319614809, 61.99277704632526, 61.668764434861785, + 61.24484474074755, 61.461440830656386, 61.276838934561326, 61.190724244624434, 61.55613660124493, + 61.12078454699415, 61.3276958600818, 61.6042219184118, 61.25513892816126, 61.31664892177688, + 61.688070650858535, 62.01190639528168, 62.08869564629636, 61.97211569824653, 62.06419901170042, + 62.32586483758532, 62.23902878995992, 62.10813808603461, 61.85578175807045, 62.23028871528028, + 62.632050281179744, 62.94816419819628, 62.73043921369783, 62.23627146727455, 62.584832680453275, + 62.62993353336442, 62.8749075179418, 62.48040775408503, 62.21373335340891, 61.79955494265226, + 61.833015907179636, 61.39273179502193, 61.269508387088344, 61.72530078527864, 62.09495330426252, + 62.151597131318645, 62.03375578347783, 62.417839543433374, 62.857582366978384, 62.57434410206618, + 63.025461247334185, 62.891637883802545, 62.74129737438404, 62.686344200077194, 62.74668907124072, + 62.8988624768576, 62.60573732388666, 63.025949401766745, 62.67167444177004, 62.90064380635685, + 62.864966632927896, 62.75573606469665, 62.64819512833622, 62.568258206957815, 62.60577268655998, + 62.15762551315059, 62.567239861872935, 62.2060758300364, 62.31900525191699, 62.28916506290531, + 62.44905922260444, 62.461296429876256, 62.500385909925505, 62.650801268022704, 62.56666212391569, + 62.40046878730333, 62.094243157050244, 62.53663695990193, 62.42300679544488, 62.36921559899188, + 62.24670139180489, 62.569517596988945, 62.66647211282815, 62.665771463959715, 62.876380887882625, + 62.45928337880216, 62.909344895815984, 62.792949925824054, 62.83068709163355, 62.69318202524612, + 62.87621607310353, 63.31283919698538, 63.25629003985129, 63.705604438529726, 63.273909526061956, + 63.5892070912608, 63.25172069842234, 63.29295380191922, 63.37827406047424, 63.40692971848919, + 62.91617622008834, 62.78539371994352, 62.67023463643756, 62.778172553545886, 62.47010093156481, + 62.73229570857713, 62.59545419552364, 63.021765325598885, 63.12625341655425, 62.78539970673421, + 62.48947067386955, 62.50019409861797, 62.27827076256643, 62.32133231940964, 62.28201148707485, + 62.75074499118775, 62.2850034814543, 62.11551149639342, 62.19275216451308, 61.99131816833137, + 61.53708598957499, 61.935909883441674, 61.982544659526965, 62.47356745991423, 62.54325753022373, + 62.146857201138765, 62.189218538018835, 62.06268691181029, 62.33603183556959, 62.82869197777662, + 63.18597742858942, 63.032993740715966, 63.5325845753775, 63.562424373715885, 63.50147245681481, + 63.9167851513779, 64.41348447594058, 64.47641931732102, 64.00422787602398, 63.84372934402268, + 63.57536630107787, 64.06767133087304, 63.857109831163605, 63.594856285335126, 64.01811161431772, + 64.07634398750827, 64.53914449236566, 64.0503925198368, 63.65098131727389, 63.68701372362301, + 63.69177696813634, 63.37477814620089, 62.9056011078514, 62.51297543133976, 62.59250225336332, + 62.80712544689635, 62.841367040210486, 63.01245999144349, 63.05987075936997, 62.7064932753452, + 62.53562962324097, 62.31063278405174, 62.266535846784436, 62.302218124559445, 62.342098988884466, + 61.85031656038063, 62.236466943962625, 62.38666354433638, 62.50448443527158, 62.2386149786552, + 62.43706094747305, 62.48167927367151, 62.53256313135768, 62.78282604117919, 62.610692060658806, + 62.60559562844521, 62.522478905756024, 62.11039441006909, 62.455676051055406, 62.12208142671207, + 62.08989132214651, 61.60556028504288, 61.72080384449528, 61.67203415718332, 61.300028102691506, + 61.2882234681282, 61.67414171824772, 61.663919195939215, 61.82126261684866, 62.14747753168045, + 61.90000454947883, 61.550477655414085, 61.823665895055534, 62.2914692558774, 61.97827489307881, + 62.373656454593686, 62.77509964777066, 63.03123026432718, 62.71959998560016, 62.798641799535915, + 62.36309802737797, 62.187803490902276, 61.86629603436246, 61.51332614924758, 61.670529134596514, + 62.01614193494398, 62.42759829956865, 62.04049015814663, 62.4879050225676, 62.534578908118796, + 62.53729183335963, 62.507213724278294, 62.91741156445245, 62.80078558969012, 62.89530940332275, + 62.82952275179085, 62.931001539540134, 62.715893011718826, 63.176668587903144, 63.25804897932602, + 63.715274804489724, 63.25110762353138, 63.703759539930466, 63.9121877763375, 63.74483403967367, + 63.278090432146044, 63.25689283100131, 63.5587547259249, 63.99611056226184, 63.711033213960846, + 63.61430481648302, 63.37762519043254, 63.825084284030204, 63.65971618383464, 64.11513361026066, + 64.55773804769903, 64.72114634405787, 64.92113863156139, 64.74299967907277, 64.58315181550863, + 64.53895512079164, 64.29203322638713, 64.35539787981175, 64.51347297698413, 64.41240397641245, + 64.37002365296716, 64.54885534394022, 65.0387758477243, 64.77700441309366, 65.18362739291737, + 65.05949296204716, 64.57684960686204, 64.47443633413799, 64.25660237904727, 64.56848853603353, + 64.13857108098799, 63.779464548765795, 63.940686810035814, 63.47280572727399, 63.75027671584932, + 63.72842205520389, 63.28631208752196, 63.54806381048302, 63.89145142689777, 63.77801877620565, + 64.22035568724904, 63.99613288560804, 64.03027098363421, 64.45100338093056, 64.4954103610469, + 64.0379570128468, 64.509809400983, 64.24776908391132, 64.18308085450447, 64.02686567211323, + 64.00949969057655, 64.08790624904579, 64.44413148646105, 64.436667344197, 64.925560935662, + 64.4380208837763, 64.63611397061874, 64.53720467943039, 64.89043109965093, 65.02933526203901, + 65.38083597755491, 65.63733460002958, 66.04889884599933, 66.15611674378638, 66.58017724018646, + 66.13733327264003, 65.69321107501534, 65.92059175869956, 65.73905913370584, 65.39714875503125, + 65.31355908128221, 65.75886616744013, 66.05775873128395, 65.93022978097021, 66.3107650269844 + ] + ] + }, + "schema": { + "fields": [ + { + "config": { + "interval": 30000 }, - "queryType": "snapshot", - "refId": "A", - "snapshot": [ - { - "data": { - "values": [ - [ - 1708679336805, - 1708679366805, - 1708679396805, - 1708679426805, - 1708679456805, - 1708679486805, - 1708679516805, - 1708679546805, - 1708679576805, - 1708679606805, - 1708679636805, - 1708679666805, - 1708679696805, - 1708679726805, - 1708679756805, - 1708679786805, - 1708679816805, - 1708679846805, - 1708679876805, - 1708679906805, - 1708679936805, - 1708679966805, - 1708679996805, - 1708680026805, - 1708680056805, - 1708680086805, - 1708680116805, - 1708680146805, - 1708680176805, - 1708680206805, - 1708680236805, - 1708680266805, - 1708680296805, - 1708680326805, - 1708680356805, - 1708680386805, - 1708680416805, - 1708680446805, - 1708680476805, - 1708680506805, - 1708680536805, - 1708680566805, - 1708680596805, - 1708680626805, - 1708680656805, - 1708680686805, - 1708680716805, - 1708680746805, - 1708680776805, - 1708680806805, - 1708680836805, - 1708680866805, - 1708680896805, - 1708680926805, - 1708680956805, - 1708680986805, - 1708681016805, - 1708681046805, - 1708681076805, - 1708681106805, - 1708681136805, - 1708681166805, - 1708681196805, - 1708681226805, - 1708681256805, - 1708681286805, - 1708681316805, - 1708681346805, - 1708681376805, - 1708681406805, - 1708681436805, - 1708681466805, - 1708681496805, - 1708681526805, - 1708681556805, - 1708681586805, - 1708681616805, - 1708681646805, - 1708681676805, - 1708681706805, - 1708681736805, - 1708681766805, - 1708681796805, - 1708681826805, - 1708681856805, - 1708681886805, - 1708681916805, - 1708681946805, - 1708681976805, - 1708682006805, - 1708682036805, - 1708682066805, - 1708682096805, - 1708682126805, - 1708682156805, - 1708682186805, - 1708682216805, - 1708682246805, - 1708682276805, - 1708682306805, - 1708682336805, - 1708682366805, - 1708682396805, - 1708682426805, - 1708682456805, - 1708682486805, - 1708682516805, - 1708682546805, - 1708682576805, - 1708682606805, - 1708682636805, - 1708682666805, - 1708682696805, - 1708682726805, - 1708682756805, - 1708682786805, - 1708682816805, - 1708682846805, - 1708682876805, - 1708682906805, - 1708682936805, - 1708682966805, - 1708682996805, - 1708683026805, - 1708683056805, - 1708683086805, - 1708683116805, - 1708683146805, - 1708683176805, - 1708683206805, - 1708683236805, - 1708683266805, - 1708683296805, - 1708683326805, - 1708683356805, - 1708683386805, - 1708683416805, - 1708683446805, - 1708683476805, - 1708683506805, - 1708683536805, - 1708683566805, - 1708683596805, - 1708683626805, - 1708683656805, - 1708683686805, - 1708683716805, - 1708683746805, - 1708683776805, - 1708683806805, - 1708683836805, - 1708683866805, - 1708683896805, - 1708683926805, - 1708683956805, - 1708683986805, - 1708684016805, - 1708684046805, - 1708684076805, - 1708684106805, - 1708684136805, - 1708684166805, - 1708684196805, - 1708684226805, - 1708684256805, - 1708684286805, - 1708684316805, - 1708684346805, - 1708684376805, - 1708684406805, - 1708684436805, - 1708684466805, - 1708684496805, - 1708684526805, - 1708684556805, - 1708684586805, - 1708684616805, - 1708684646805, - 1708684676805, - 1708684706805, - 1708684736805, - 1708684766805, - 1708684796805, - 1708684826805, - 1708684856805, - 1708684886805, - 1708684916805, - 1708684946805, - 1708684976805, - 1708685006805, - 1708685036805, - 1708685066805, - 1708685096805, - 1708685126805, - 1708685156805, - 1708685186805, - 1708685216805, - 1708685246805, - 1708685276805, - 1708685306805, - 1708685336805, - 1708685366805, - 1708685396805, - 1708685426805, - 1708685456805, - 1708685486805, - 1708685516805, - 1708685546805, - 1708685576805, - 1708685606805, - 1708685636805, - 1708685666805, - 1708685696805, - 1708685726805, - 1708685756805, - 1708685786805, - 1708685816805, - 1708685846805, - 1708685876805, - 1708685906805, - 1708685936805, - 1708685966805, - 1708685996805, - 1708686026805, - 1708686056805, - 1708686086805, - 1708686116805, - 1708686146805, - 1708686176805, - 1708686206805, - 1708686236805, - 1708686266805, - 1708686296805, - 1708686326805, - 1708686356805, - 1708686386805, - 1708686416805, - 1708686446805, - 1708686476805, - 1708686506805, - 1708686536805, - 1708686566805, - 1708686596805, - 1708686626805, - 1708686656805, - 1708686686805, - 1708686716805, - 1708686746805, - 1708686776805, - 1708686806805, - 1708686836805, - 1708686866805, - 1708686896805, - 1708686926805, - 1708686956805, - 1708686986805, - 1708687016805, - 1708687046805, - 1708687076805, - 1708687106805, - 1708687136805, - 1708687166805, - 1708687196805, - 1708687226805, - 1708687256805, - 1708687286805, - 1708687316805, - 1708687346805, - 1708687376805, - 1708687406805, - 1708687436805, - 1708687466805, - 1708687496805, - 1708687526805, - 1708687556805, - 1708687586805, - 1708687616805, - 1708687646805, - 1708687676805, - 1708687706805, - 1708687736805, - 1708687766805, - 1708687796805, - 1708687826805, - 1708687856805, - 1708687886805, - 1708687916805, - 1708687946805, - 1708687976805, - 1708688006805, - 1708688036805, - 1708688066805, - 1708688096805, - 1708688126805, - 1708688156805, - 1708688186805, - 1708688216805, - 1708688246805, - 1708688276805, - 1708688306805, - 1708688336805, - 1708688366805, - 1708688396805, - 1708688426805, - 1708688456805, - 1708688486805, - 1708688516805, - 1708688546805, - 1708688576805, - 1708688606805, - 1708688636805, - 1708688666805, - 1708688696805, - 1708688726805, - 1708688756805, - 1708688786805, - 1708688816805, - 1708688846805, - 1708688876805, - 1708688906805, - 1708688936805, - 1708688966805, - 1708688996805, - 1708689026805, - 1708689056805, - 1708689086805, - 1708689116805, - 1708689146805, - 1708689176805, - 1708689206805, - 1708689236805, - 1708689266805, - 1708689296805, - 1708689326805, - 1708689356805, - 1708689386805, - 1708689416805, - 1708689446805, - 1708689476805, - 1708689506805, - 1708689536805, - 1708689566805, - 1708689596805, - 1708689626805, - 1708689656805, - 1708689686805, - 1708689716805, - 1708689746805, - 1708689776805, - 1708689806805, - 1708689836805, - 1708689866805, - 1708689896805, - 1708689926805, - 1708689956805, - 1708689986805, - 1708690016805, - 1708690046805, - 1708690076805, - 1708690106805, - 1708690136805, - 1708690166805, - 1708690196805, - 1708690226805, - 1708690256805, - 1708690286805, - 1708690316805, - 1708690346805, - 1708690376805, - 1708690406805, - 1708690436805, - 1708690466805, - 1708690496805, - 1708690526805, - 1708690556805, - 1708690586805, - 1708690616805, - 1708690646805, - 1708690676805, - 1708690706805, - 1708690736805, - 1708690766805, - 1708690796805, - 1708690826805, - 1708690856805, - 1708690886805, - 1708690916805, - 1708690946805, - 1708690976805, - 1708691006805, - 1708691036805, - 1708691066805, - 1708691096805, - 1708691126805, - 1708691156805, - 1708691186805, - 1708691216805, - 1708691246805, - 1708691276805, - 1708691306805, - 1708691336805, - 1708691366805, - 1708691396805, - 1708691426805, - 1708691456805, - 1708691486805, - 1708691516805, - 1708691546805, - 1708691576805, - 1708691606805, - 1708691636805, - 1708691666805, - 1708691696805, - 1708691726805, - 1708691756805, - 1708691786805, - 1708691816805, - 1708691846805, - 1708691876805, - 1708691906805, - 1708691936805, - 1708691966805, - 1708691996805, - 1708692026805, - 1708692056805, - 1708692086805, - 1708692116805, - 1708692146805, - 1708692176805, - 1708692206805, - 1708692236805, - 1708692266805, - 1708692296805, - 1708692326805, - 1708692356805, - 1708692386805, - 1708692416805, - 1708692446805, - 1708692476805, - 1708692506805, - 1708692536805, - 1708692566805, - 1708692596805, - 1708692626805, - 1708692656805, - 1708692686805, - 1708692716805, - 1708692746805, - 1708692776805, - 1708692806805, - 1708692836805, - 1708692866805, - 1708692896805, - 1708692926805, - 1708692956805, - 1708692986805, - 1708693016805, - 1708693046805, - 1708693076805, - 1708693106805, - 1708693136805, - 1708693166805, - 1708693196805, - 1708693226805, - 1708693256805, - 1708693286805, - 1708693316805, - 1708693346805, - 1708693376805, - 1708693406805, - 1708693436805, - 1708693466805, - 1708693496805, - 1708693526805, - 1708693556805, - 1708693586805, - 1708693616805, - 1708693646805, - 1708693676805, - 1708693706805, - 1708693736805, - 1708693766805, - 1708693796805, - 1708693826805, - 1708693856805, - 1708693886805, - 1708693916805, - 1708693946805, - 1708693976805, - 1708694006805, - 1708694036805, - 1708694066805, - 1708694096805, - 1708694126805, - 1708694156805, - 1708694186805, - 1708694216805, - 1708694246805, - 1708694276805, - 1708694306805, - 1708694336805, - 1708694366805, - 1708694396805, - 1708694426805, - 1708694456805, - 1708694486805, - 1708694516805, - 1708694546805, - 1708694576805, - 1708694606805, - 1708694636805, - 1708694666805, - 1708694696805, - 1708694726805, - 1708694756805, - 1708694786805, - 1708694816805, - 1708694846805, - 1708694876805, - 1708694906805, - 1708694936805, - 1708694966805, - 1708694996805, - 1708695026805, - 1708695056805, - 1708695086805, - 1708695116805, - 1708695146805, - 1708695176805, - 1708695206805, - 1708695236805, - 1708695266805, - 1708695296805, - 1708695326805, - 1708695356805, - 1708695386805, - 1708695416805, - 1708695446805, - 1708695476805, - 1708695506805, - 1708695536805, - 1708695566805, - 1708695596805, - 1708695626805, - 1708695656805, - 1708695686805, - 1708695716805, - 1708695746805, - 1708695776805, - 1708695806805, - 1708695836805, - 1708695866805, - 1708695896805, - 1708695926805, - 1708695956805, - 1708695986805, - 1708696016805, - 1708696046805, - 1708696076805, - 1708696106805, - 1708696136805, - 1708696166805, - 1708696196805, - 1708696226805, - 1708696256805, - 1708696286805, - 1708696316805, - 1708696346805, - 1708696376805, - 1708696406805, - 1708696436805, - 1708696466805, - 1708696496805, - 1708696526805, - 1708696556805, - 1708696586805, - 1708696616805, - 1708696646805, - 1708696676805, - 1708696706805, - 1708696736805, - 1708696766805, - 1708696796805, - 1708696826805, - 1708696856805, - 1708696886805, - 1708696916805, - 1708696946805, - 1708696976805, - 1708697006805, - 1708697036805, - 1708697066805, - 1708697096805, - 1708697126805, - 1708697156805, - 1708697186805, - 1708697216805, - 1708697246805, - 1708697276805, - 1708697306805, - 1708697336805, - 1708697366805, - 1708697396805, - 1708697426805, - 1708697456805, - 1708697486805, - 1708697516805, - 1708697546805, - 1708697576805, - 1708697606805, - 1708697636805, - 1708697666805, - 1708697696805, - 1708697726805, - 1708697756805, - 1708697786805, - 1708697816805, - 1708697846805, - 1708697876805, - 1708697906805, - 1708697936805, - 1708697966805, - 1708697996805, - 1708698026805, - 1708698056805, - 1708698086805, - 1708698116805, - 1708698146805, - 1708698176805, - 1708698206805, - 1708698236805, - 1708698266805, - 1708698296805, - 1708698326805, - 1708698356805, - 1708698386805, - 1708698416805, - 1708698446805, - 1708698476805, - 1708698506805, - 1708698536805, - 1708698566805, - 1708698596805, - 1708698626805, - 1708698656805, - 1708698686805, - 1708698716805, - 1708698746805, - 1708698776805, - 1708698806805, - 1708698836805, - 1708698866805, - 1708698896805, - 1708698926805, - 1708698956805, - 1708698986805, - 1708699016805, - 1708699046805, - 1708699076805, - 1708699106805, - 1708699136805, - 1708699166805, - 1708699196805, - 1708699226805, - 1708699256805, - 1708699286805, - 1708699316805, - 1708699346805, - 1708699376805, - 1708699406805, - 1708699436805, - 1708699466805, - 1708699496805, - 1708699526805, - 1708699556805, - 1708699586805, - 1708699616805, - 1708699646805, - 1708699676805, - 1708699706805, - 1708699736805, - 1708699766805, - 1708699796805, - 1708699826805, - 1708699856805, - 1708699886805, - 1708699916805, - 1708699946805, - 1708699976805, - 1708700006805, - 1708700036805, - 1708700066805, - 1708700096805, - 1708700126805, - 1708700156805, - 1708700186805, - 1708700216805, - 1708700246805, - 1708700276805, - 1708700306805, - 1708700336805, - 1708700366805, - 1708700396805, - 1708700426805, - 1708700456805, - 1708700486805, - 1708700516805, - 1708700546805, - 1708700576805, - 1708700606805, - 1708700636805, - 1708700666805, - 1708700696805, - 1708700726805, - 1708700756805, - 1708700786805, - 1708700816805, - 1708700846805, - 1708700876805, - 1708700906805 - ], - [ - 61.7880220958275, - 61.81270791445456, - 61.829208768453746, - 61.3952804521061, - 61.54378204663586, - 61.793292376739025, - 61.89508181687579, - 62.032346031223305, - 62.090954811114734, - 61.788446359641156, - 61.34681406228381, - 61.37147422434034, - 61.40948125782848, - 61.69144945060843, - 61.790001610516114, - 61.9726511399688, - 62.17186395369245, - 61.841593279449114, - 61.81351577982158, - 61.774548608665306, - 61.45071912098117, - 61.837381696515884, - 61.65059114296433, - 61.1939165193637, - 61.251406089255795, - 60.90456801094042, - 60.56801269725712, - 60.79736902333104, - 60.9367183948152, - 61.40785835680261, - 61.621326272755965, - 61.53478423246078, - 61.616180375313114, - 61.56950269911591, - 61.419724817297585, - 61.76631225002593, - 62.12897242834343, - 62.123394668856335, - 61.84384351878861, - 61.784342739352965, - 61.946359707479985, - 62.00854561243485, - 61.949378148238175, - 61.83251536870273, - 62.10046102360543, - 62.51955528279164, - 62.43194543555232, - 61.93563724102479, - 61.61222776112464, - 61.706572968014584, - 61.25653135192282, - 61.356339636867965, - 61.699918128409436, - 61.700832273918536, - 61.48680261436916, - 61.23885493750151, - 60.88064816100224, - 60.8061115971784, - 60.48689168723215, - 60.55420316390225, - 60.78326551379002, - 60.71763616746389, - 61.0384027799684, - 61.361681117105356, - 61.819694140841825, - 61.60303629180282, - 61.13531779096821, - 61.59247554276454, - 62.073700396192514, - 62.257393021269124, - 62.433793965222456, - 62.250955190991135, - 61.85027922321473, - 61.991414140632, - 62.103475488499456, - 62.1675085033716, - 61.81759701584563, - 61.44204189561965, - 61.39004247589699, - 61.84841370788845, - 62.01982884908544, - 62.29261477385842, - 62.1881407732118, - 61.77974858108549, - 61.30269030921595, - 61.72885233653658, - 62.160061928899516, - 62.13127060574461, - 61.99860014382346, - 62.2713524353439, - 62.45360209624644, - 62.45033536766769, - 62.74579940152534, - 62.652379007396135, - 62.50461375528075, - 62.4923739782267, - 62.87608071230181, - 62.979684959589086, - 63.376138214369306, - 63.66354651661235, - 63.77873383844567, - 64.02365055931571, - 63.57333739418103, - 63.569251641994995, - 63.51940188659398, - 63.81887126104792, - 64.10808860762592, - 64.26214829221084, - 63.81353904099011, - 63.792123977174654, - 64.09459044217124, - 64.49651926575704, - 64.87380353415439, - 65.22102686208456, - 65.49078940759729, - 65.83529933530703, - 66.06313506256244, - 66.36902912666964, - 66.78048161957578, - 66.85326316970041, - 67.14736719324404, - 67.55927886414139, - 67.497025942169, - 67.84593513490577, - 67.47441764273138, - 67.94871645670011, - 68.32729017349827, - 68.0857872436968, - 67.93500201308075, - 67.75781760651351, - 67.62321694227796, - 67.87687655718156, - 68.03974600191707, - 67.82627187210018, - 68.29863099170737, - 67.95842239244082, - 67.56173819267319, - 67.11089985276264, - 67.58721546400895, - 67.43817707268052, - 67.63734474285276, - 67.8608900819446, - 68.2491098921388, - 68.64222598287019, - 68.93436090180123, - 68.58516897681044, - 68.88556663186037, - 68.82509258379442, - 68.43122435581691, - 68.01151775860062, - 68.31539501569645, - 68.01224489043588, - 68.21383479637454, - 67.85864255481124, - 67.85285437130011, - 67.68770174333852, - 67.78834746198736, - 68.07096889676643, - 68.06340992932907, - 68.37116739265392, - 68.25606363201948, - 68.0308569092854, - 68.0279418148109, - 67.68837311892736, - 67.98015788941186, - 68.39001231797856, - 68.55810426314746, - 68.67055538403375, - 68.24669275435873, - 68.3542775530211, - 67.99792233705742, - 67.93848132742042, - 67.54327434886798, - 67.86541640645122, - 67.63715332453567, - 67.44863373545599, - 67.54029786855584, - 67.3799227903252, - 67.272800723832, - 67.65752571426705, - 67.27217330871126, - 67.36259068699385, - 67.6920166495926, - 67.99237921892899, - 68.00526928940994, - 67.67665255949264, - 67.82106088034847, - 68.22225474161398, - 68.17103508007338, - 68.1016199190692, - 67.9715626706774, - 68.32706440829391, - 67.87634330533211, - 67.97207720874553, - 67.87493382264455, - 67.38357737752105, - 66.94768029712087, - 66.67493510394992, - 66.48367657533245, - 66.77035937087237, - 66.87685661575208, - 66.68590889792685, - 67.11545823298368, - 67.39896920212514, - 67.34979904565203, - 67.19769898467558, - 67.45512207826779, - 67.07906680752531, - 67.41541723819202, - 67.15472798048341, - 67.15672773264464, - 67.6290223056695, - 68.11240707910342, - 68.40594352628086, - 67.92798893549167, - 67.46919856375305, - 67.4835115803936, - 67.63236807133978, - 67.17328415938867, - 67.24458272746313, - 66.84593455247185, - 67.01430262963748, - 67.05291762336604, - 67.23817875420809, - 66.98941509949245, - 66.68717338595964, - 67.12826784930103, - 66.74586515933792, - 67.12397362624425, - 66.91465424208462, - 66.68655917540693, - 66.19881496899525, - 66.68817057925634, - 66.37134623059349, - 66.596971028669, - 67.08641642499875, - 67.12758650919066, - 67.38518121414808, - 67.46997153732326, - 67.33145553844577, - 67.65363406004056, - 67.45595043797628, - 67.24978988019829, - 67.39677514651268, - 67.5978323405599, - 67.96140308646021, - 68.00477049936622, - 67.75104436464927, - 67.99988077010511, - 68.19366044789288, - 68.49848829432483, - 68.25947149341026, - 68.6317723773926, - 68.91668608614779, - 68.8159923502771, - 69.17513974647629, - 69.23356802652235, - 69.25309375298245, - 69.21125949874141, - 69.20726323550102, - 68.78019883289588, - 69.24667107728621, - 69.1153426259508, - 69.56206231608063, - 69.72141422867662, - 69.541238907092, - 69.81884141217625, - 69.53432369593423, - 69.43315031151812, - 69.01688148574168, - 68.90923934889648, - 69.13664462290991, - 69.31984528071725, - 69.14875943039056, - 69.48885652654923, - 69.88297168977773, - 69.42462273878934, - 69.12679413148601, - 68.98123233159377, - 68.7762758442149, - 68.48846507574687, - 68.02689832504595, - 68.02852643078998, - 67.555096330903, - 67.4639788904284, - 67.61277291577984, - 67.70121565906206, - 67.66817331556194, - 67.84767253849441, - 68.18538578924706, - 68.29860737971661, - 68.53693780239855, - 68.59722904625475, - 68.72061222893264, - 68.6709989466837, - 69.09400533816238, - 69.20295511428635, - 69.04258759148102, - 68.77139467473914, - 68.57756213054044, - 68.67449080482191, - 68.60860533532343, - 69.01676017621124, - 69.3052161196497, - 68.89017381442457, - 68.87423335356772, - 68.94718162265079, - 69.07199142609447, - 68.79769510366016, - 68.65106890757201, - 68.77703094552813, - 68.28258034480257, - 68.39479364216955, - 68.34220719427985, - 68.64253416645396, - 68.87725555362057, - 68.5131252571485, - 68.89127035251008, - 68.67886068136877, - 68.4447624537287, - 68.00181105736998, - 68.16998112143412, - 68.43591872875314, - 68.02705208136817, - 68.45211203302853, - 68.53289570629535, - 68.58200283313724, - 68.25718933019785, - 68.36370956465593, - 68.05020228727913, - 68.50658655636543, - 68.66449948753682, - 68.2241732330651, - 68.09791466418139, - 67.88940531760399, - 68.36978817923638, - 67.96433264274425, - 68.36349840881765, - 68.48435193949715, - 68.79844380614031, - 69.02017361165895, - 68.52624119834393, - 68.74005315812674, - 68.76067653657759, - 68.27918960695276, - 68.65714329915559, - 68.42872809873309, - 67.98192094490678, - 68.02723949195821, - 68.36423058076824, - 68.71132274325691, - 68.6730162618873, - 68.2039400216969, - 67.86554691576616, - 67.49988053949725, - 67.16307215146469, - 67.0353669125758, - 67.25486895820083, - 66.95629780399439, - 67.11477592840751, - 66.64323746050167, - 66.20269172026232, - 66.48026858749327, - 66.4824015740555, - 66.09931551662274, - 66.52915019033718, - 66.45576884782787, - 66.24667785942938, - 65.81418399593981, - 65.5611498094969, - 65.3398959293035, - 65.77646026507297, - 65.39346792956597, - 65.41692492067247, - 65.80888038895401, - 66.01134945900266, - 65.80681809031556, - 65.32295296805074, - 65.37373854104783, - 64.93533382859144, - 64.7705026812803, - 65.17813748381224, - 65.361732144628, - 64.9021692008158, - 64.7544901225187, - 64.89471070898715, - 64.74868721773261, - 65.00087272748908, - 65.13124433830268, - 64.86532668113782, - 64.51108629312537, - 64.48208116580882, - 64.86340417937541, - 64.5927354669956, - 64.1183990008181, - 63.66603997870095, - 63.670535114339415, - 63.179312479687646, - 62.71414451358941, - 62.349677869382184, - 62.73321111245177, - 62.63668992149067, - 62.246656820357686, - 62.12151749467506, - 62.09114940207692, - 62.13652644719604, - 62.59170540130276, - 62.28435878005047, - 62.62555470862962, - 62.73559453227453, - 62.63492310358647, - 62.99493118502586, - 63.2621260239913, - 62.99279716454684, - 62.8629723908099, - 63.01324605242161, - 63.27456767760901, - 63.11585082867133, - 63.04279509050621, - 63.10999097814922, - 63.544265738869036, - 63.11822935137409, - 63.01523903936018, - 63.20403674621687, - 62.83896086980938, - 62.41843196460615, - 62.11985404869429, - 62.07986319614809, - 61.99277704632526, - 61.668764434861785, - 61.24484474074755, - 61.461440830656386, - 61.276838934561326, - 61.190724244624434, - 61.55613660124493, - 61.12078454699415, - 61.3276958600818, - 61.6042219184118, - 61.25513892816126, - 61.31664892177688, - 61.688070650858535, - 62.01190639528168, - 62.08869564629636, - 61.97211569824653, - 62.06419901170042, - 62.32586483758532, - 62.23902878995992, - 62.10813808603461, - 61.85578175807045, - 62.23028871528028, - 62.632050281179744, - 62.94816419819628, - 62.73043921369783, - 62.23627146727455, - 62.584832680453275, - 62.62993353336442, - 62.8749075179418, - 62.48040775408503, - 62.21373335340891, - 61.79955494265226, - 61.833015907179636, - 61.39273179502193, - 61.269508387088344, - 61.72530078527864, - 62.09495330426252, - 62.151597131318645, - 62.03375578347783, - 62.417839543433374, - 62.857582366978384, - 62.57434410206618, - 63.025461247334185, - 62.891637883802545, - 62.74129737438404, - 62.686344200077194, - 62.74668907124072, - 62.8988624768576, - 62.60573732388666, - 63.025949401766745, - 62.67167444177004, - 62.90064380635685, - 62.864966632927896, - 62.75573606469665, - 62.64819512833622, - 62.568258206957815, - 62.60577268655998, - 62.15762551315059, - 62.567239861872935, - 62.2060758300364, - 62.31900525191699, - 62.28916506290531, - 62.44905922260444, - 62.461296429876256, - 62.500385909925505, - 62.650801268022704, - 62.56666212391569, - 62.40046878730333, - 62.094243157050244, - 62.53663695990193, - 62.42300679544488, - 62.36921559899188, - 62.24670139180489, - 62.569517596988945, - 62.66647211282815, - 62.665771463959715, - 62.876380887882625, - 62.45928337880216, - 62.909344895815984, - 62.792949925824054, - 62.83068709163355, - 62.69318202524612, - 62.87621607310353, - 63.31283919698538, - 63.25629003985129, - 63.705604438529726, - 63.273909526061956, - 63.5892070912608, - 63.25172069842234, - 63.29295380191922, - 63.37827406047424, - 63.40692971848919, - 62.91617622008834, - 62.78539371994352, - 62.67023463643756, - 62.778172553545886, - 62.47010093156481, - 62.73229570857713, - 62.59545419552364, - 63.021765325598885, - 63.12625341655425, - 62.78539970673421, - 62.48947067386955, - 62.50019409861797, - 62.27827076256643, - 62.32133231940964, - 62.28201148707485, - 62.75074499118775, - 62.2850034814543, - 62.11551149639342, - 62.19275216451308, - 61.99131816833137, - 61.53708598957499, - 61.935909883441674, - 61.982544659526965, - 62.47356745991423, - 62.54325753022373, - 62.146857201138765, - 62.189218538018835, - 62.06268691181029, - 62.33603183556959, - 62.82869197777662, - 63.18597742858942, - 63.032993740715966, - 63.5325845753775, - 63.562424373715885, - 63.50147245681481, - 63.9167851513779, - 64.41348447594058, - 64.47641931732102, - 64.00422787602398, - 63.84372934402268, - 63.57536630107787, - 64.06767133087304, - 63.857109831163605, - 63.594856285335126, - 64.01811161431772, - 64.07634398750827, - 64.53914449236566, - 64.0503925198368, - 63.65098131727389, - 63.68701372362301, - 63.69177696813634, - 63.37477814620089, - 62.9056011078514, - 62.51297543133976, - 62.59250225336332, - 62.80712544689635, - 62.841367040210486, - 63.01245999144349, - 63.05987075936997, - 62.7064932753452, - 62.53562962324097, - 62.31063278405174, - 62.266535846784436, - 62.302218124559445, - 62.342098988884466, - 61.85031656038063, - 62.236466943962625, - 62.38666354433638, - 62.50448443527158, - 62.2386149786552, - 62.43706094747305, - 62.48167927367151, - 62.53256313135768, - 62.78282604117919, - 62.610692060658806, - 62.60559562844521, - 62.522478905756024, - 62.11039441006909, - 62.455676051055406, - 62.12208142671207, - 62.08989132214651, - 61.60556028504288, - 61.72080384449528, - 61.67203415718332, - 61.300028102691506, - 61.2882234681282, - 61.67414171824772, - 61.663919195939215, - 61.82126261684866, - 62.14747753168045, - 61.90000454947883, - 61.550477655414085, - 61.823665895055534, - 62.2914692558774, - 61.97827489307881, - 62.373656454593686, - 62.77509964777066, - 63.03123026432718, - 62.71959998560016, - 62.798641799535915, - 62.36309802737797, - 62.187803490902276, - 61.86629603436246, - 61.51332614924758, - 61.670529134596514, - 62.01614193494398, - 62.42759829956865, - 62.04049015814663, - 62.4879050225676, - 62.534578908118796, - 62.53729183335963, - 62.507213724278294, - 62.91741156445245, - 62.80078558969012, - 62.89530940332275, - 62.82952275179085, - 62.931001539540134, - 62.715893011718826, - 63.176668587903144, - 63.25804897932602, - 63.715274804489724, - 63.25110762353138, - 63.703759539930466, - 63.9121877763375, - 63.74483403967367, - 63.278090432146044, - 63.25689283100131, - 63.5587547259249, - 63.99611056226184, - 63.711033213960846, - 63.61430481648302, - 63.37762519043254, - 63.825084284030204, - 63.65971618383464, - 64.11513361026066, - 64.55773804769903, - 64.72114634405787, - 64.92113863156139, - 64.74299967907277, - 64.58315181550863, - 64.53895512079164, - 64.29203322638713, - 64.35539787981175, - 64.51347297698413, - 64.41240397641245, - 64.37002365296716, - 64.54885534394022, - 65.0387758477243, - 64.77700441309366, - 65.18362739291737, - 65.05949296204716, - 64.57684960686204, - 64.47443633413799, - 64.25660237904727, - 64.56848853603353, - 64.13857108098799, - 63.779464548765795, - 63.940686810035814, - 63.47280572727399, - 63.75027671584932, - 63.72842205520389, - 63.28631208752196, - 63.54806381048302, - 63.89145142689777, - 63.77801877620565, - 64.22035568724904, - 63.99613288560804, - 64.03027098363421, - 64.45100338093056, - 64.4954103610469, - 64.0379570128468, - 64.509809400983, - 64.24776908391132, - 64.18308085450447, - 64.02686567211323, - 64.00949969057655, - 64.08790624904579, - 64.44413148646105, - 64.436667344197, - 64.925560935662, - 64.4380208837763, - 64.63611397061874, - 64.53720467943039, - 64.89043109965093, - 65.02933526203901, - 65.38083597755491, - 65.63733460002958, - 66.04889884599933, - 66.15611674378638, - 66.58017724018646, - 66.13733327264003, - 65.69321107501534, - 65.92059175869956, - 65.73905913370584, - 65.39714875503125, - 65.31355908128221, - 65.75886616744013, - 66.05775873128395, - 65.93022978097021, - 66.3107650269844 - ] - ] - }, - "schema": { - "fields": [ - { - "config": { - "interval": 30000 - }, - "name": "time", - "type": "time", - "typeInfo": { - "frame": "time.Time", - "nullable": true - } - }, - { - "config": {}, - "labels": {}, - "name": "A-hehwzd", - "type": "number", - "typeInfo": { - "frame": "float64", - "nullable": true - } - } - ], - "meta": { - "custom": { - "customStat": 10 - }, - "typeVersion": [ - 0, - 0 - ] - }, - "refId": "A" - } - } - ] - } - ], - "title": "Canvas Enhanced Data Links", - "transformations": [ - { - "id": "calculateField", - "options": { - "alias": "Count (transformation)", - "mode": "reduceRow", - "reduce": { - "include": [ - "A-series" - ], - "reducer": "count" - } + "name": "time", + "type": "time", + "typeInfo": { + "frame": "time.Time", + "nullable": true } - } - ], - "type": "canvas" + }, + { + "config": {}, + "labels": {}, + "name": "A-hehwzd", + "type": "number", + "typeInfo": { + "frame": "float64", + "nullable": true + } + } + ], + "meta": { + "custom": { + "customStat": 10 + }, + "typeVersion": [0, 0] + }, + "refId": "A" + } + } + ] } - ], - "schemaVersion": 39, - "tags": [ - "gdev", - "panel-tests", - "canvas" - ], - "templating": { - "list": [] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timeRangeUpdatedDuringEditOrView": false, - "timepicker": {}, - "timezone": "browser", - "title": "Panel Tests - Canvas Datalinks", - "uid": "adf95uwu7w1s0e", - "version": 10, - "weekStart": "" -} \ No newline at end of file + ], + "title": "Canvas Enhanced Data Links", + "transformations": [ + { + "id": "calculateField", + "options": { + "alias": "Count (transformation)", + "mode": "reduceRow", + "reduce": { + "include": ["A-series"], + "reducer": "count" + } + } + } + ], + "type": "canvas" + } + ], + "schemaVersion": 39, + "tags": ["gdev", "panel-tests", "canvas"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "browser", + "title": "Panel Tests - Canvas Datalinks", + "uid": "adf95uwu7w1s0e", + "version": 10, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/panel-canvas/canvas-examples.json b/devenv/dev-dashboards/panel-canvas/canvas-examples.json index 7b025da055f..0d09602f4f6 100644 --- a/devenv/dev-dashboards/panel-canvas/canvas-examples.json +++ b/devenv/dev-dashboards/panel-canvas/canvas-examples.json @@ -50,7 +50,9 @@ }, "type": "canvas", "title": "Wind Energy", - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "pluginVersion": "9.4.0-pre", "fieldConfig": { "defaults": { @@ -1132,8 +1134,7 @@ { "csvContent": "w1_rpm, w1_energy_output, w1_status, w2_rpm, w2_energy_output, w2_status, w3_rpm, w3_energy_output, w3_status, w4_rpm, w4_energy_output, w4_status\n12, 1.2, operational, 22, 1.8, operational, 8, 0.6, operational, 0, 0, needs maintenance", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -1141,7 +1142,9 @@ ] }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 15, "w": 6, @@ -1176,7 +1179,9 @@ "type": "row" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -3310,8 +3315,7 @@ { "csvContent": "Status_12, Status_13, Status_14, Throughput_12, Throughput_13, Throughput_14, Color_12, Color_13, Color_14\nReady, Blocked, Ready, 205, 0, 205, red, Red, Green", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "Conveyor Controller", "scenarioId": "csv_content" @@ -3321,7 +3325,9 @@ "type": "canvas" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 18, "w": 6, @@ -3356,7 +3362,9 @@ "type": "row" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -3582,8 +3590,7 @@ { "csvContent": "house_draw, battery_charge, solar_output\n1.1, 2.2, 3.3", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -3593,7 +3600,9 @@ "type": "canvas" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 15, "w": 6, @@ -3615,7 +3624,9 @@ "type": "text" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -3792,8 +3803,7 @@ { "csvContent": "solar_output, battery_draw\n0.0, 2.2", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -3803,7 +3813,9 @@ "type": "canvas" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 15, "w": 6, @@ -3826,11 +3838,7 @@ } ], "schemaVersion": 37, - "tags": [ - "gdev", - "panel-tests", - "canvas" - ], + "tags": ["gdev", "panel-tests", "canvas"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-common/color_modes.json b/devenv/dev-dashboards/panel-common/color_modes.json index 2e5f6922d61..4e80e2a2216 100644 --- a/devenv/dev-dashboards/panel-common/color_modes.json +++ b/devenv/dev-dashboards/panel-common/color_modes.json @@ -18,7 +18,9 @@ "links": [], "panels": [ { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -217,7 +219,9 @@ "type": "stat" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { diff --git a/devenv/dev-dashboards/panel-common/lazy_loading.json b/devenv/dev-dashboards/panel-common/lazy_loading.json index 960c466124c..e6a1a91ad4f 100644 --- a/devenv/dev-dashboards/panel-common/lazy_loading.json +++ b/devenv/dev-dashboards/panel-common/lazy_loading.json @@ -18,7 +18,9 @@ "links": [], "panels": [ { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 7, "w": 24, @@ -143,7 +145,9 @@ "colorBackground": false, "colorValue": true, "colors": ["#299c46", "#73BF69", "#d44a3a"], - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": null, "format": "ms", "gauge": { @@ -222,7 +226,9 @@ "colorBackground": false, "colorValue": true, "colors": ["#299c46", "#73BF69", "#d44a3a"], - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": null, "format": "ms", "gauge": { @@ -301,7 +307,9 @@ "colorBackground": false, "colorValue": true, "colors": ["#299c46", "#73BF69", "#d44a3a"], - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": null, "format": "ms", "gauge": { @@ -380,7 +388,9 @@ "colorBackground": false, "colorValue": true, "colors": ["#299c46", "#73BF69", "#d44a3a"], - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": null, "format": "ms", "gauge": { @@ -464,7 +474,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 3, "fillGradient": 0, "gridPos": { @@ -561,7 +573,9 @@ } }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 4, "w": 24, @@ -617,7 +631,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -706,7 +722,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -795,7 +813,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -884,7 +904,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -976,7 +998,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 3, "fillGradient": 0, "gridPos": { @@ -1080,7 +1104,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 10, "gridPos": { @@ -1167,7 +1193,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 8, "gridPos": { @@ -1256,7 +1284,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -1345,7 +1375,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -1434,7 +1466,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -1523,7 +1557,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -1615,7 +1651,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 3, "fillGradient": 0, "gridPos": { @@ -1719,7 +1757,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -1808,7 +1848,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -1897,7 +1939,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -1986,7 +2030,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "fillGradient": 5, "gridPos": { @@ -2069,7 +2115,9 @@ } }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 7, "w": 24, diff --git a/devenv/dev-dashboards/panel-common/linked-viz.json b/devenv/dev-dashboards/panel-common/linked-viz.json index 5b0201e8392..9be073d012c 100644 --- a/devenv/dev-dashboards/panel-common/linked-viz.json +++ b/devenv/dev-dashboards/panel-common/linked-viz.json @@ -63,9 +63,7 @@ "justifyMode": "auto", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -128,9 +126,7 @@ "justifyMode": "auto", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -199,9 +195,7 @@ "justifyMode": "auto", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -259,9 +253,7 @@ "justifyMode": "auto", "orientation": "auto", "reduceOptions": { - "calcs": [ - "first" - ], + "calcs": ["first"], "fields": "", "limit": 2, "values": true @@ -325,9 +317,7 @@ "justifyMode": "auto", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "limit": 2, "values": true @@ -397,9 +387,7 @@ "justifyMode": "auto", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "limit": 2, "values": true @@ -455,9 +443,7 @@ "options": { "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -518,9 +504,7 @@ "options": { "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -586,9 +570,7 @@ "options": { "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -644,9 +626,7 @@ "options": { "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "limit": 2, "values": true @@ -708,9 +688,7 @@ "options": { "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "limit": 2, "values": true @@ -777,9 +755,7 @@ "options": { "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "limit": 2, "values": true @@ -837,9 +813,7 @@ "displayMode": "gradient", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -900,9 +874,7 @@ "displayMode": "gradient", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -968,9 +940,7 @@ "displayMode": "gradient", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -1026,9 +996,7 @@ "displayMode": "gradient", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "limit": 2, "values": true @@ -1090,9 +1058,7 @@ "displayMode": "gradient", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "limit": 2, "values": true @@ -1159,9 +1125,7 @@ "displayMode": "gradient", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "limit": 2, "values": true diff --git a/devenv/dev-dashboards/panel-common/shared_queries.json b/devenv/dev-dashboards/panel-common/shared_queries.json index 2117b83a399..e2d8528c10f 100644 --- a/devenv/dev-dashboards/panel-common/shared_queries.json +++ b/devenv/dev-dashboards/panel-common/shared_queries.json @@ -22,7 +22,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -99,8 +101,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -108,8 +109,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_metric_values", @@ -117,8 +117,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "csv_metric_values", @@ -166,9 +165,7 @@ "options": { "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -227,9 +224,7 @@ "options": { "orientation": "auto", "reduceOptions": { - "calcs": [ - "min" - ], + "calcs": ["min"], "fields": "", "values": false }, @@ -295,9 +290,7 @@ "minVizWidth": 0, "orientation": "vertical", "reduceOptions": { - "calcs": [ - "max" - ], + "calcs": ["max"], "fields": "", "values": false }, @@ -386,9 +379,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true, @@ -419,10 +410,7 @@ ], "refresh": "", "schemaVersion": 38, - "tags": [ - "gdev", - "datasource-test" - ], + "tags": ["gdev", "datasource-test"], "templating": { "list": [] }, @@ -431,18 +419,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Datasource tests - Shared Queries", diff --git a/devenv/dev-dashboards/panel-dashlist/dashlist.json b/devenv/dev-dashboards/panel-dashlist/dashlist.json index 8db5b616acd..9ba4f9ce5a2 100644 --- a/devenv/dev-dashboards/panel-dashlist/dashlist.json +++ b/devenv/dev-dashboards/panel-dashlist/dashlist.json @@ -24,8 +24,7 @@ "panels": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "gridPos": { "h": 12, diff --git a/devenv/dev-dashboards/panel-datagrid/datagrid_metric_values.json b/devenv/dev-dashboards/panel-datagrid/datagrid_metric_values.json index 00a65a9f1aa..b4180bb04ea 100644 --- a/devenv/dev-dashboards/panel-datagrid/datagrid_metric_values.json +++ b/devenv/dev-dashboards/panel-datagrid/datagrid_metric_values.json @@ -22,7 +22,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 8, "w": 12, @@ -36,8 +38,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -45,8 +46,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_metric_values", @@ -73,4 +73,4 @@ "version": 0, "uid": "c01bf42b-b783-4447-a304-8554cee1843b", "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-flamegraph/panel_tests_flame_graph.json b/devenv/dev-dashboards/panel-flamegraph/panel_tests_flame_graph.json index efd4ecdb27c..d4716f38a93 100644 --- a/devenv/dev-dashboards/panel-flamegraph/panel_tests_flame_graph.json +++ b/devenv/dev-dashboards/panel-flamegraph/panel_tests_flame_graph.json @@ -28,7 +28,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 14, "w": 24, @@ -39,8 +41,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "flame_graph" @@ -53,10 +54,7 @@ "refresh": "", "revision": 1, "schemaVersion": 38, - "tags": [ - "gdev", - "panel-tests" - ], + "tags": ["gdev", "panel-tests"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json b/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json index f7ff80edc12..e5c9daa44ea 100644 --- a/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json +++ b/devenv/dev-dashboards/panel-gauge/gauge-multi-series.json @@ -18,7 +18,9 @@ "links": [], "panels": [ { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 8, "w": 20, @@ -105,7 +107,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 28, "w": 4, @@ -177,7 +181,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 20, "w": 20, diff --git a/devenv/dev-dashboards/panel-gauge/gauge_tests.json b/devenv/dev-dashboards/panel-gauge/gauge_tests.json index 309a255fcc7..0c8e2651e67 100644 --- a/devenv/dev-dashboards/panel-gauge/gauge_tests.json +++ b/devenv/dev-dashboards/panel-gauge/gauge_tests.json @@ -33,7 +33,9 @@ "type": "row" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -102,7 +104,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -171,7 +175,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -240,7 +246,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -309,7 +317,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -378,7 +388,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -447,7 +459,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -604,7 +618,9 @@ "type": "row" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -683,7 +699,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "should read N/A", "fieldConfig": { "defaults": { @@ -763,7 +781,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "should read N/A", "fieldConfig": { "defaults": { @@ -843,7 +863,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "should read N/A", "fieldConfig": { "defaults": { @@ -946,7 +968,9 @@ "type": "row" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1024,7 +1048,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1103,7 +1129,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1182,7 +1210,9 @@ "type": "gauge" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { diff --git a/devenv/dev-dashboards/panel-geomap/geomap-color-field.json b/devenv/dev-dashboards/panel-geomap/geomap-color-field.json index 38ea1d23b39..e12a4a7210d 100644 --- a/devenv/dev-dashboards/panel-geomap/geomap-color-field.json +++ b/devenv/dev-dashboards/panel-geomap/geomap-color-field.json @@ -28,7 +28,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -135,8 +137,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -306,9 +307,7 @@ "justifyMode": "auto", "orientation": "auto", "reduceOptions": { - "calcs": [ - "min" - ], + "calcs": ["min"], "fields": "", "values": false }, @@ -367,9 +366,7 @@ "justifyMode": "auto", "orientation": "auto", "reduceOptions": { - "calcs": [ - "max" - ], + "calcs": ["max"], "fields": "", "values": false }, @@ -455,9 +452,7 @@ "options": { "footer": { "fields": "", - "reducer": [ - "max" - ], + "reducer": ["max"], "show": true }, "showHeader": true @@ -478,11 +473,7 @@ } ], "schemaVersion": 37, - "tags": [ - "gdev", - "panel-tests", - "geomap" - ], + "tags": ["gdev", "panel-tests", "geomap"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-geomap/geomap-photo-layer.json b/devenv/dev-dashboards/panel-geomap/geomap-photo-layer.json index af192c66d59..1722a91c739 100644 --- a/devenv/dev-dashboards/panel-geomap/geomap-photo-layer.json +++ b/devenv/dev-dashboards/panel-geomap/geomap-photo-layer.json @@ -29,7 +29,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -117,8 +119,7 @@ { "csvContent": "state,src\nAL,https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Flag_of_Alabama.svg/320px-Flag_of_Alabama.svg.png\nAK,https://upload.wikimedia.org/wikipedia/commons/thumb/e/e6/Flag_of_Alaska.svg/320px-Flag_of_Alaska.svg.png\nAZ,https://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Flag_of_Arizona.svg/320px-Flag_of_Arizona.svg.png\nAR,https://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Flag_of_Arkansas.svg/320px-Flag_of_Arkansas.svg.png\nCA,https://upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_California.svg/320px-Flag_of_California.svg.png\nCO,https://upload.wikimedia.org/wikipedia/commons/thumb/4/46/Flag_of_Colorado.svg/320px-Flag_of_Colorado.svg.png\nCT,https://upload.wikimedia.org/wikipedia/commons/thumb/9/96/Flag_of_Connecticut.svg/304px-Flag_of_Connecticut.svg.png\nDE,https://upload.wikimedia.org/wikipedia/commons/thumb/c/c6/Flag_of_Delaware.svg/320px-Flag_of_Delaware.svg.png\nFL,https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Flag_of_Florida.svg/320px-Flag_of_Florida.svg.png\nGA,https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Georgia_%28U.S._state%29.svg/320px-Flag_of_Georgia_%28U.S._state%29.svg.png\nHI,https://upload.wikimedia.org/wikipedia/commons/thumb/e/ef/Flag_of_Hawaii.svg/320px-Flag_of_Hawaii.svg.png\nID,https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Flag_of_Idaho.svg/305px-Flag_of_Idaho.svg.png\nIL,https://upload.wikimedia.org/wikipedia/commons/thumb/0/01/Flag_of_Illinois.svg/320px-Flag_of_Illinois.svg.png\nIN,https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Flag_of_Indiana.svg/320px-Flag_of_Indiana.svg.png\nIA,https://upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Flag_of_Iowa.svg/320px-Flag_of_Iowa.svg.png\nKS,https://upload.wikimedia.org/wikipedia/commons/thumb/d/da/Flag_of_Kansas.svg/320px-Flag_of_Kansas.svg.png\nKY,https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/Flag_of_Kentucky.svg/320px-Flag_of_Kentucky.svg.png\nLA,https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Flag_of_Louisiana.svg/320px-Flag_of_Louisiana.svg.png\nME,https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/Flag_of_the_State_of_Maine.svg/305px-Flag_of_the_State_of_Maine.svg.png\nMD,https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/Flag_of_Maryland.svg/320px-Flag_of_Maryland.svg.png\nMA,https://upload.wikimedia.org/wikipedia/commons/thumb/f/f2/Flag_of_Massachusetts.svg/320px-Flag_of_Massachusetts.svg.png\nMI,https://upload.wikimedia.org/wikipedia/commons/thumb/b/b5/Flag_of_Michigan.svg/320px-Flag_of_Michigan.svg.png\nMN,https://upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Flag_of_Minnesota.svg/320px-Flag_of_Minnesota.svg.png\nMS,https://upload.wikimedia.org/wikipedia/commons/thumb/4/42/Flag_of_Mississippi.svg/320px-Flag_of_Mississippi.svg.png\nMO,https://upload.wikimedia.org/wikipedia/commons/thumb/5/5a/Flag_of_Missouri.svg/320px-Flag_of_Missouri.svg.png\nMT,https://upload.wikimedia.org/wikipedia/commons/thumb/c/cb/Flag_of_Montana.svg/320px-Flag_of_Montana.svg.png\nNE,https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Flag_of_Nebraska.svg/320px-Flag_of_Nebraska.svg.png\nNV,https://upload.wikimedia.org/wikipedia/commons/thumb/f/f1/Flag_of_Nevada.svg/320px-Flag_of_Nevada.svg.png\nNH,https://upload.wikimedia.org/wikipedia/commons/thumb/2/28/Flag_of_New_Hampshire.svg/320px-Flag_of_New_Hampshire.svg.png\nNJ,https://upload.wikimedia.org/wikipedia/commons/thumb/9/92/Flag_of_New_Jersey.svg/320px-Flag_of_New_Jersey.svg.png\nNM,https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_New_Mexico.svg/320px-Flag_of_New_Mexico.svg.png\nNY,https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Flag_of_New_York.svg/320px-Flag_of_New_York.svg.png\nNC,https://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Flag_of_North_Carolina.svg/320px-Flag_of_North_Carolina.svg.png\nND,https://upload.wikimedia.org/wikipedia/commons/thumb/e/ee/Flag_of_North_Dakota.svg/305px-Flag_of_North_Dakota.svg.png\nOH,https://upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Flag_of_Ohio.svg/320px-Flag_of_Ohio.svg.png\nOK,https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Flag_of_Oklahoma.svg/320px-Flag_of_Oklahoma.svg.png\nOR,https://upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Flag_of_Oregon.svg/320px-Flag_of_Oregon.svg.png\nPA,https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Flag_of_Pennsylvania.svg/320px-Flag_of_Pennsylvania.svg.png\nRI,https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/Flag_of_Rhode_Island.svg/273px-Flag_of_Rhode_Island.svg.png\nSC,https://upload.wikimedia.org/wikipedia/commons/thumb/6/69/Flag_of_South_Carolina.svg/320px-Flag_of_South_Carolina.svg.png\nSD,https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Flag_of_South_Dakota.svg/320px-Flag_of_South_Dakota.svg.png\nTN,https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/Flag_of_Tennessee.svg/320px-Flag_of_Tennessee.svg.png\nTX,https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Flag_of_Texas.svg/320px-Flag_of_Texas.svg.png\nUT,https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Flag_of_Utah.svg/320px-Flag_of_Utah.svg.png\nVT,https://upload.wikimedia.org/wikipedia/commons/thumb/4/49/Flag_of_Vermont.svg/320px-Flag_of_Vermont.svg.png\nVA,https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/Flag_of_Virginia.svg/320px-Flag_of_Virginia.svg.png\nWA,https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Flag_of_Washington.svg/320px-Flag_of_Washington.svg.png\nWV,https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Flag_of_West_Virginia.svg/320px-Flag_of_West_Virginia.svg.png\nWI,https://upload.wikimedia.org/wikipedia/commons/thumb/2/22/Flag_of_Wisconsin.svg/320px-Flag_of_Wisconsin.svg.png\nWY,https://upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Flag_of_Wyoming.svg/320px-Flag_of_Wyoming.svg.png\n", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" diff --git a/devenv/dev-dashboards/panel-geomap/geomap-route-layer.json b/devenv/dev-dashboards/panel-geomap/geomap-route-layer.json index 169ca0163c5..8307558b992 100644 --- a/devenv/dev-dashboards/panel-geomap/geomap-route-layer.json +++ b/devenv/dev-dashboards/panel-geomap/geomap-route-layer.json @@ -76,8 +76,7 @@ { "csvContent": "lat,lon,val\n-5,2,0\n1,5,25\n6,10,50\n9,15,75\n10,20,100", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -86,13 +85,14 @@ "scenarioId": "csv_content", "refId": "B", "datasource": { - "uid": "PD8C576611E62080A", "type": "grafana-testdata-datasource" }, "csvContent": "lat,lon,alt\n45,0,0\n40,5,5\n35,10,10\n30,15, 15\n25,20,20" } ], - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "options": { "view": { "allLayers": true, diff --git a/devenv/dev-dashboards/panel-geomap/geomap-spatial-operations-transformer.json b/devenv/dev-dashboards/panel-geomap/geomap-spatial-operations-transformer.json index 0bd5732b577..64fd0589c45 100644 --- a/devenv/dev-dashboards/panel-geomap/geomap-spatial-operations-transformer.json +++ b/devenv/dev-dashboards/panel-geomap/geomap-spatial-operations-transformer.json @@ -28,7 +28,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -88,8 +90,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -100,11 +101,7 @@ } ], "schemaVersion": 37, - "tags": [ - "gdev", - "panel-tests", - "geomap" - ], + "tags": ["gdev", "panel-tests", "geomap"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-geomap/geomap-v91.json b/devenv/dev-dashboards/panel-geomap/geomap-v91.json index d9a928ceba1..1bec4865743 100644 --- a/devenv/dev-dashboards/panel-geomap/geomap-v91.json +++ b/devenv/dev-dashboards/panel-geomap/geomap-v91.json @@ -28,7 +28,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -106,8 +108,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -115,8 +116,7 @@ { "csvFileName": "population_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_file" @@ -126,7 +126,9 @@ "type": "geomap" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -209,7 +211,9 @@ "type": "geomap" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -311,8 +315,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "simulation", @@ -329,11 +332,7 @@ } ], "schemaVersion": 37, - "tags": [ - "gdev", - "panel-tests", - "geomap" - ], + "tags": ["gdev", "panel-tests", "geomap"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-geomap/geomap_multi-layers.json b/devenv/dev-dashboards/panel-geomap/geomap_multi-layers.json index 8cecfcab846..e7be37b67a2 100644 --- a/devenv/dev-dashboards/panel-geomap/geomap_multi-layers.json +++ b/devenv/dev-dashboards/panel-geomap/geomap_multi-layers.json @@ -29,7 +29,7 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -319,7 +319,7 @@ "type": "geomap" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -431,11 +431,7 @@ } ], "schemaVersion": 36, - "tags": [ - "gdev", - "panel-tests", - "geomap" - ], + "tags": ["gdev", "panel-tests", "geomap"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-geomap/panel-geomap.json b/devenv/dev-dashboards/panel-geomap/panel-geomap.json index 70f1595204f..bda6377ffff 100644 --- a/devenv/dev-dashboards/panel-geomap/panel-geomap.json +++ b/devenv/dev-dashboards/panel-geomap/panel-geomap.json @@ -28,7 +28,7 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -127,7 +127,7 @@ "type": "geomap" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -230,7 +230,7 @@ "type": "geomap" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -325,7 +325,7 @@ "type": "geomap" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -427,11 +427,7 @@ ], "refresh": "", "schemaVersion": 37, - "tags": [ - "gdev", - "panel-tests", - "geomap" - ], + "tags": ["gdev", "panel-tests", "geomap"], "templating": { "list": [] }, @@ -440,17 +436,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - Geomap", diff --git a/devenv/dev-dashboards/panel-graph/graph-shared-tooltips.json b/devenv/dev-dashboards/panel-graph/graph-shared-tooltips.json index 187d61b443f..6c06ee04db5 100644 --- a/devenv/dev-dashboards/panel-graph/graph-shared-tooltips.json +++ b/devenv/dev-dashboards/panel-graph/graph-shared-tooltips.json @@ -759,11 +759,7 @@ ], "refresh": false, "schemaVersion": 28, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-graph/graph-time-regions.json b/devenv/dev-dashboards/panel-graph/graph-time-regions.json index a4e03148536..c9661011d36 100644 --- a/devenv/dev-dashboards/panel-graph/graph-time-regions.json +++ b/devenv/dev-dashboards/panel-graph/graph-time-regions.json @@ -22,7 +22,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 2, "gridPos": { "h": 8, @@ -118,7 +120,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 2, "gridPos": { "h": 8, @@ -219,7 +223,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 2, "gridPos": { "h": 8, @@ -375,7 +381,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 2, "gridPos": { "h": 8, @@ -469,7 +477,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 8, diff --git a/devenv/dev-dashboards/panel-graph/graph_tests.json b/devenv/dev-dashboards/panel-graph/graph_tests.json index bd1fc95d3d6..fa214400e77 100644 --- a/devenv/dev-dashboards/panel-graph/graph_tests.json +++ b/devenv/dev-dashboards/panel-graph/graph_tests.json @@ -22,7 +22,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fill": 1, @@ -108,7 +110,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fill": 1, @@ -194,7 +198,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fill": 1, @@ -280,7 +286,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fill": 1, @@ -382,7 +390,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fill": 1, @@ -495,7 +505,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fill": 1, @@ -581,7 +593,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fill": 1, @@ -683,7 +697,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fill": 1, @@ -807,7 +823,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fill": 1, @@ -932,7 +950,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fill": 0, @@ -1049,7 +1069,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": 3, "fill": 1, "gridPos": { @@ -1134,7 +1156,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": 3, "fill": 1, "gridPos": { @@ -1237,7 +1261,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": 3, "fill": 1, "gridPos": { @@ -1376,7 +1402,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": 3, "fill": 1, "gridPos": { @@ -1480,7 +1508,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": 3, "fill": 1, "gridPos": { diff --git a/devenv/dev-dashboards/panel-heatmap/heatmap-calculate-log.json b/devenv/dev-dashboards/panel-heatmap/heatmap-calculate-log.json index f48358f7a02..26764802b06 100644 --- a/devenv/dev-dashboards/panel-heatmap/heatmap-calculate-log.json +++ b/devenv/dev-dashboards/panel-heatmap/heatmap-calculate-log.json @@ -28,7 +28,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -101,8 +103,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "max": 500000, "min": 0.01, @@ -807,11 +808,7 @@ } ], "schemaVersion": 36, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-heatmap/heatmap-legacy.json b/devenv/dev-dashboards/panel-heatmap/heatmap-legacy.json index d42005d2a59..03a4c72a0e9 100644 --- a/devenv/dev-dashboards/panel-heatmap/heatmap-legacy.json +++ b/devenv/dev-dashboards/panel-heatmap/heatmap-legacy.json @@ -40,7 +40,9 @@ "mode": "opacity" }, "dataFormat": "tsbuckets", - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 11, "w": 7, @@ -58,8 +60,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "exponential_heatmap_bucket_data" @@ -93,7 +94,9 @@ "mode": "spectrum" }, "dataFormat": "timeseries", - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 11, "w": 9, @@ -111,8 +114,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "max": 70, "min": 10, @@ -151,7 +153,9 @@ "mode": "spectrum" }, "dataFormat": "timeseries", - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 11, "w": 8, @@ -169,8 +173,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "exponential_heatmap_bucket_data" @@ -204,7 +207,9 @@ "mode": "opacity" }, "dataFormat": "tsbuckets", - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 11, "w": 7, @@ -222,8 +227,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "exponential_heatmap_bucket_data" @@ -315,7 +319,9 @@ "mode": "spectrum" }, "dataFormat": "timeseries", - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 11, "w": 8, @@ -333,8 +339,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "exponential_heatmap_bucket_data" @@ -360,11 +365,7 @@ } ], "schemaVersion": 36, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-heatmap/heatmap-x.json b/devenv/dev-dashboards/panel-heatmap/heatmap-x.json index ab4a499b23c..a43e78ddb69 100644 --- a/devenv/dev-dashboards/panel-heatmap/heatmap-x.json +++ b/devenv/dev-dashboards/panel-heatmap/heatmap-x.json @@ -1,324 +1,315 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 116, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 10, - "x": 0, - "y": 0 - }, - "id": 2, - "options": { - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "9.4.0-pre", - "targets": [ - { - "csvContent": "x,y1,y2\n1,8,12\n2,6,13\n3,7,9\n5,9,7\n6,5,9", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Raw heatmap rows", - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "scaleDistribution": { - "type": "linear" - } - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 14, - "x": 10, - "y": 0 - }, - "id": 4, - "options": { - "calculate": false, - "cellGap": 1, - "color": { - "exponent": 0.5, - "fill": "dark-orange", - "mode": "scheme", - "reverse": false, - "scale": "exponential", - "scheme": "Oranges", - "steps": 64 - }, - "exemplars": { - "color": "rgba(255,0,255,0.7)" - }, - "filterValues": { - "le": 1e-9 - }, - "legend": { - "show": true - }, - "rowsFrame": { - "layout": "auto" - }, - "tooltip": { - "show": true, - "yHistogram": false - }, - "yAxis": { - "axisPlacement": "left", - "reverse": false - } - }, - "pluginVersion": "9.4.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A" - } - ], - "title": "Row heatmap", - "type": "heatmap" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 10, - "x": 0, - "y": 9 - }, - "id": 5, - "options": { - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "9.4.0-pre", - "targets": [ - { - "csvContent": "x,y,count\n1,4,10\n1,6,11\n2,5,30\n2,4,22\n3,6,17", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Raw heatmap cells", - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "scaleDistribution": { - "type": "linear" - } - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 14, - "x": 10, - "y": 9 - }, - "id": 6, - "options": { - "calculate": false, - "cellGap": 1, - "color": { - "exponent": 0.5, - "fill": "dark-orange", - "mode": "scheme", - "reverse": false, - "scale": "exponential", - "scheme": "Oranges", - "steps": 64 - }, - "exemplars": { - "color": "rgba(255,0,255,0.7)" - }, - "filterValues": { - "le": 1e-9 - }, - "legend": { - "show": true - }, - "rowsFrame": { - "layout": "auto" - }, - "tooltip": { - "show": true, - "yHistogram": false - }, - "yAxis": { - "axisPlacement": "left", - "reverse": false - } - }, - "pluginVersion": "9.4.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 5, - "refId": "A" - } - ], - "title": "Cells heatmap", - "type": "heatmap" + "type": "dashboard" } - ], - "schemaVersion": 37, - "tags": [], - "templating": { - "list": [] + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 116, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 10, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "csvContent": "x,y1,y2\n1,8,12\n2,6,13\n3,7,9\n5,9,7\n6,5,9", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Raw heatmap rows", + "type": "table" }, - "time": { - "from": "now-6h", - "to": "now" + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 14, + "x": 10, + "y": 0 + }, + "id": 4, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "show": true, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "title": "Row heatmap", + "type": "heatmap" }, - "timepicker": {}, - "timezone": "", - "title": "Heatmap X axis", - "uid": "5Y0jv6pVz", - "version": 3, - "weekStart": "" - } - + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 10, + "x": 0, + "y": 9 + }, + "id": 5, + "options": { + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "csvContent": "x,y,count\n1,4,10\n1,6,11\n2,5,30\n2,4,22\n3,6,17", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Raw heatmap cells", + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 14, + "x": 10, + "y": 9 + }, + "id": 6, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "show": true, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 5, + "refId": "A" + } + ], + "title": "Cells heatmap", + "type": "heatmap" + } + ], + "schemaVersion": 37, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Heatmap X axis", + "uid": "5Y0jv6pVz", + "version": 3, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/panel-histogram/histogram_tests.json b/devenv/dev-dashboards/panel-histogram/histogram_tests.json index eb5d6374971..af6127cb447 100644 --- a/devenv/dev-dashboards/panel-histogram/histogram_tests.json +++ b/devenv/dev-dashboards/panel-histogram/histogram_tests.json @@ -28,7 +28,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -72,9 +74,7 @@ "bucketOffset": 0, "combine": false, "legend": { - "calcs": [ - "sum" - ], + "calcs": ["sum"], "displayMode": "list", "placement": "bottom", "showLegend": true @@ -83,8 +83,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -143,9 +142,7 @@ "bucketSize": 3, "combine": false, "legend": { - "calcs": [ - "sum" - ], + "calcs": ["sum"], "displayMode": "list", "placement": "bottom", "showLegend": true @@ -165,7 +162,9 @@ "type": "histogram" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -210,9 +209,7 @@ "bucketSize": 1, "combine": false, "legend": { - "calcs": [ - "sum" - ], + "calcs": ["sum"], "displayMode": "list", "placement": "bottom", "showLegend": true @@ -222,8 +219,7 @@ { "csvFileName": "weight_height.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -235,9 +231,7 @@ "id": "filterFieldsByName", "options": { "include": { - "names": [ - "Height" - ] + "names": ["Height"] } } } @@ -245,7 +239,9 @@ "type": "histogram" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -290,9 +286,7 @@ "bucketSize": 5, "combine": false, "legend": { - "calcs": [ - "sum" - ], + "calcs": ["sum"], "displayMode": "list", "placement": "bottom", "showLegend": true @@ -302,8 +296,7 @@ { "csvFileName": "weight_height.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -315,9 +308,7 @@ "id": "filterFieldsByName", "options": { "include": { - "names": [ - "Weight" - ] + "names": ["Weight"] } } } @@ -325,7 +316,9 @@ "type": "histogram" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -367,9 +360,7 @@ "footer": { "countRows": false, "fields": [], - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true, @@ -385,8 +376,7 @@ { "csvFileName": "weight_height.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -399,9 +389,7 @@ "id": "filterFieldsByName", "options": { "include": { - "names": [ - "Height" - ] + "names": ["Height"] } } }, @@ -417,7 +405,9 @@ "type": "table" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -459,9 +449,7 @@ "footer": { "countRows": false, "fields": [], - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -471,8 +459,7 @@ { "csvFileName": "weight_height.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -484,9 +471,7 @@ "id": "filterFieldsByName", "options": { "include": { - "names": [ - "Weight" - ] + "names": ["Weight"] } } }, @@ -501,7 +486,9 @@ "type": "table" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -560,9 +547,7 @@ "options": { "bucketOffset": 0, "legend": { - "calcs": [ - "sum" - ], + "calcs": ["sum"], "displayMode": "list", "placement": "bottom", "showLegend": true @@ -572,8 +557,7 @@ { "csvContent": "xMin,xMax,count1,abcd\n1,2,10,123\n2,3,20,265\n3,4,30,73\n4,5,10,24", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -583,7 +567,9 @@ "type": "histogram" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -642,9 +628,7 @@ "options": { "bucketOffset": 0, "legend": { - "calcs": [ - "sum" - ], + "calcs": ["sum"], "displayMode": "list", "placement": "bottom", "showLegend": true @@ -654,8 +638,7 @@ { "csvContent": "xMin,count1,abcd\n1,10,123\n2,20,265\n3,30,73\n4,10,24", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -665,7 +648,9 @@ "type": "histogram" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -724,9 +709,7 @@ "options": { "bucketOffset": 0, "legend": { - "calcs": [ - "sum" - ], + "calcs": ["sum"], "displayMode": "list", "placement": "bottom", "showLegend": true @@ -736,8 +719,7 @@ { "csvContent": "xMax,count1,abcd\n1,10,123\n2,20,265\n3,30,73\n4,10,24", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -748,8 +730,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -802,8 +783,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"custom\": {\n \"resultType\": \"matrix\"\n },\n \"type\": \"heatmap-rows\",\n \"typeVersion\": [\n 0,\n 1\n ]\n },\n \"name\": \"0.005\",\n \"fields\": [\n {\n \"config\": {\n \"interval\": 1200000\n },\n \"name\": \"Time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"0.005\"\n },\n \"labels\": {\n \"le\": \"0.005\"\n },\n \"name\": \"0.005\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"0.01\"\n },\n \"labels\": {\n \"le\": \"0.01\"\n },\n \"name\": \"0.01\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"0.025\"\n },\n \"labels\": {\n \"le\": \"0.025\"\n },\n \"name\": \"0.025\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"0.05\"\n },\n \"labels\": {\n \"le\": \"0.05\"\n },\n \"name\": \"0.05\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"0.1\"\n },\n \"labels\": {\n \"le\": \"0.1\"\n },\n \"name\": \"0.1\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"0.25\"\n },\n \"labels\": {\n \"le\": \"0.25\"\n },\n \"name\": \"0.25\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"0.5\"\n },\n \"labels\": {\n \"le\": \"0.5\"\n },\n \"name\": \"0.5\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"1.0\"\n },\n \"labels\": {\n \"le\": \"1.0\"\n },\n \"name\": \"1.0\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"2.5\"\n },\n \"labels\": {\n \"le\": \"2.5\"\n },\n \"name\": \"2.5\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"5.0\"\n },\n \"labels\": {\n \"le\": \"5.0\"\n },\n \"name\": \"5.0\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"10.0\"\n },\n \"labels\": {\n \"le\": \"10.0\"\n },\n \"name\": \"10.0\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"25.0\"\n },\n \"labels\": {\n \"le\": \"25.0\"\n },\n \"name\": \"25.0\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"50.0\"\n },\n \"labels\": {\n \"le\": \"50.0\"\n },\n \"name\": \"50.0\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"100.0\"\n },\n \"labels\": {\n \"le\": \"100.0\"\n },\n \"name\": \"100.0\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n },\n {\n \"config\": {\n \"displayNameFromDS\": \"+Inf\"\n },\n \"labels\": {\n \"le\": \"+Inf\"\n },\n \"name\": \"+Inf\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1706456400000,\n 1706457600000,\n 1706458800000,\n 1706460000000,\n 1706461200000,\n 1706462400000,\n 1706463600000,\n 1706464800000,\n 1706466000000,\n 1706467200000,\n 1706468400000,\n 1706469600000,\n 1706470800000,\n 1706472000000,\n 1706473200000,\n 1706474400000,\n 1706475600000,\n 1706476800000,\n 1706478000000\n ],\n [\n 0.19357429718875502,\n 0.18072289156626506,\n 0.18313253012048192,\n 0.18955823293172688,\n 0.18634538152610441,\n 0.19518072289156624,\n 0.20080321285140562,\n 0.18313253012048192,\n 0.19678714859437751,\n 0.18795180722891563,\n 0.18473895582329317,\n 0.19357429718875502,\n 0.19116465863453813,\n 0.19196787148594374,\n 0.19437751004016063,\n 0.19759036144578312,\n 0.19839357429718874,\n 0.19357429718875502,\n 0.18634538152610441\n ],\n [\n 0.22248995983935738,\n 0.229718875502008,\n 0.22248995983935738,\n 0.22168674698795174,\n 0.2305220883534136,\n 0.21285140562248994,\n 0.2128514056224899,\n 0.22891566265060237,\n 0.22570281124497987,\n 0.22088353413654616,\n 0.22088353413654618,\n 0.21927710843373488,\n 0.21686746987951805,\n 0.22248995983935738,\n 0.21847389558232927,\n 0.21124497991967867,\n 0.216867469879518,\n 0.2200803212851405,\n 0.22329317269076304\n ],\n [\n 0.017670682730923704,\n 0.02329317269076303,\n 0.027309236947791138,\n 0.02168674698795181,\n 0.016867469879518093,\n 0.025702811244979917,\n 0.02008032128514059,\n 0.018473895582329314,\n 0.01124497991967871,\n 0.024899598393574307,\n 0.025702811244979862,\n 0.0208835341365462,\n 0.02409638554216864,\n 0.01927710843373498,\n 0.0208835341365462,\n 0.02248995983935742,\n 0.016867469879518093,\n 0.02008032128514059,\n 0.02329317269076303\n ],\n [\n 0,\n 0,\n 0.0008032128514056658,\n 0.0008032128514056658,\n 0,\n 0,\n 0,\n 0.0032128514056224966,\n 0,\n 0,\n 0.0024096385542168863,\n 0,\n 0.001606425702811276,\n 0,\n 0,\n 0.0024096385542168863,\n 0.001606425702811276,\n 0,\n 0.0008032128514056103\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ],\n [\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665,\n 0.06666666666666665\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ]\n ]\n }\n }\n]", "refId": "A", @@ -815,8 +795,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -882,8 +861,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"type\": \"heatmap-cells\",\n \"typeVersion\": [\n 0,\n 0\n ],\n \"executedQueryString\": \"Expr: sum(rate(cortex_request_duration_seconds{container=~\\\"compactor\\\", route=~\\\"(debug_pprof|metrics|ready)\\\"}[4m0s]))\\nStep: 1m0s\",\n \"preferredVisualisationType\": \"graph\"\n },\n \"fields\": [\n {\n \"name\": \"xMax\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\"\n },\n \"config\": {\n \"interval\": 60000\n }\n },\n {\n \"name\": \"yMin\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n },\n \"labels\": {},\n \"config\": {\n \"displayNameFromDS\": \"sum(rate(cortex_request_duration_seconds{container=~\\\"compactor\\\", route=~\\\"(debug_pprof|metrics|ready)\\\"}[4m0s]))\"\n }\n },\n {\n \"name\": \"yMax\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n },\n \"config\": {}\n },\n {\n \"name\": \"count\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\"\n },\n \"config\": {}\n },\n {\n \"name\": \"yLayout\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"int8\"\n },\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483460000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483520000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483580000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483640000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483700000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000,\n 1706483760000\n ],\n [\n 0.000012831061023768835,\n 0.000013992371264719713,\n 0.000016639827463764308,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.00002566212204753767,\n 0.000039576402424652394,\n 0.00020529697638030136,\n 0.0002238779402355154,\n 0.00026623723942022893,\n 0.0002903337683112112,\n 0.00031661121939721915,\n 0.00048828125,\n 0.002532889755177753,\n 0.002762135864009951,\n 0.00390625,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.009290680585958758,\n 0.010131559020711013,\n 0.012048522073499537,\n 0.014328188175072986,\n 0.01703918332289465,\n 0.018581361171917516,\n 0.020263118041422026,\n 13.45434264405943,\n 0.000012831061023768835,\n 0.000013992371264719713,\n 0.000016639827463764308,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.00002566212204753767,\n 0.000039576402424652394,\n 0.0002238779402355154,\n 0.000244140625,\n 0.00026623723942022893,\n 0.0002903337683112112,\n 0.00048828125,\n 0.0021298979153618314,\n 0.002532889755177753,\n 0.002762135864009951,\n 0.00390625,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.009290680585958758,\n 0.010131559020711013,\n 0.011048543456039804,\n 0.012048522073499537,\n 0.014328188175072986,\n 0.01703918332289465,\n 13.45434264405943,\n 0.000012831061023768835,\n 0.000013992371264719713,\n 0.000016639827463764308,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.00002566212204753767,\n 0.000244140625,\n 0.00026623723942022893,\n 0.0002903337683112112,\n 0.0021298979153618314,\n 0.002532889755177753,\n 0.002762135864009951,\n 0.00390625,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.009290680585958758,\n 0.010131559020711013,\n 0.011048543456039804,\n 0.012048522073499537,\n 0.014328188175072986,\n 0.01703918332289465,\n 13.45434264405943,\n 0.000012831061023768835,\n 0.000013992371264719713,\n 0.0000152587890625,\n 0.000016639827463764308,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.00002566212204753767,\n 0.000244140625,\n 0.00026623723942022893,\n 0.0002903337683112112,\n 0.0021298979153618314,\n 0.002762135864009951,\n 0.00390625,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.008519591661447326,\n 0.009290680585958758,\n 0.010131559020711013,\n 0.011048543456039804,\n 0.012048522073499537,\n 0.014328188175072986,\n 0.01703918332289465,\n 13.45434264405943,\n 0.000012831061023768835,\n 0.000013992371264719713,\n 0.0000152587890625,\n 0.000016639827463764308,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.00002566212204753767,\n 0.000244140625,\n 0.00026623723942022893,\n 0.0002903337683112112,\n 0.0005806675366224224,\n 0.0021298979153618314,\n 0.002762135864009951,\n 0.00390625,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.008519591661447326,\n 0.009290680585958758,\n 0.010131559020711013,\n 0.011048543456039804,\n 0.015625,\n 13.45434264405943,\n 0.000012831061023768835,\n 0.000013992371264719713,\n 0.0000152587890625,\n 0.000016639827463764308,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.00002566212204753767,\n 0.000244140625,\n 0.00026623723942022893,\n 0.0002903337683112112,\n 0.00031661121939721915,\n 0.0003452669830012439,\n 0.0005806675366224224,\n 0.0021298979153618314,\n 0.002762135864009951,\n 0.00390625,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.008519591661447326,\n 0.009290680585958758,\n 0.010131559020711013,\n 0.011048543456039804,\n 0.015625,\n 13.45434264405943\n ],\n [\n 0.000013992371264719713,\n 0.0000152587890625,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.000023532269674803783,\n 0.000027984742529439426,\n 0.000043158372875155485,\n 0.0002238779402355154,\n 0.000244140625,\n 0.0002903337683112112,\n 0.00031661121939721915,\n 0.0003452669830012439,\n 0.0005324744788404579,\n 0.002762135864009951,\n 0.0030121305183748843,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.008519591661447326,\n 0.010131559020711013,\n 0.011048543456039804,\n 0.013139006488339287,\n 0.015625,\n 0.018581361171917516,\n 0.020263118041422026,\n 0.022097086912079608,\n 14.672064691274738,\n 0.000013992371264719713,\n 0.0000152587890625,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.000023532269674803783,\n 0.000027984742529439426,\n 0.000043158372875155485,\n 0.000244140625,\n 0.00026623723942022893,\n 0.0002903337683112112,\n 0.00031661121939721915,\n 0.0005324744788404579,\n 0.0023226701464896895,\n 0.002762135864009951,\n 0.0030121305183748843,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.008519591661447326,\n 0.010131559020711013,\n 0.011048543456039804,\n 0.012048522073499537,\n 0.013139006488339287,\n 0.015625,\n 0.018581361171917516,\n 14.672064691274738,\n 0.000013992371264719713,\n 0.0000152587890625,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.000023532269674803783,\n 0.000027984742529439426,\n 0.00026623723942022893,\n 0.0002903337683112112,\n 0.00031661121939721915,\n 0.0023226701464896895,\n 0.002762135864009951,\n 0.0030121305183748843,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.008519591661447326,\n 0.010131559020711013,\n 0.011048543456039804,\n 0.012048522073499537,\n 0.013139006488339287,\n 0.015625,\n 0.018581361171917516,\n 14.672064691274738,\n 0.000013992371264719713,\n 0.0000152587890625,\n 0.000016639827463764308,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.000023532269674803783,\n 0.000027984742529439426,\n 0.00026623723942022893,\n 0.0002903337683112112,\n 0.00031661121939721915,\n 0.0023226701464896895,\n 0.0030121305183748843,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.008519591661447326,\n 0.009290680585958758,\n 0.010131559020711013,\n 0.011048543456039804,\n 0.012048522073499537,\n 0.013139006488339287,\n 0.015625,\n 0.018581361171917516,\n 14.672064691274738,\n 0.000013992371264719713,\n 0.0000152587890625,\n 0.000016639827463764308,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.000023532269674803783,\n 0.000027984742529439426,\n 0.00026623723942022893,\n 0.0002903337683112112,\n 0.00031661121939721915,\n 0.0006332224387944383,\n 0.0023226701464896895,\n 0.0030121305183748843,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.008519591661447326,\n 0.009290680585958758,\n 0.010131559020711013,\n 0.011048543456039804,\n 0.012048522073499537,\n 0.01703918332289465,\n 14.672064691274738,\n 0.000013992371264719713,\n 0.0000152587890625,\n 0.000016639827463764308,\n 0.0000181458605194507,\n 0.000019788201212326197,\n 0.000021579186437577742,\n 0.000023532269674803783,\n 0.000027984742529439426,\n 0.00026623723942022893,\n 0.0002903337683112112,\n 0.00031661121939721915,\n 0.0003452669830012439,\n 0.00037651631479686053,\n 0.0006332224387944383,\n 0.0023226701464896895,\n 0.0030121305183748843,\n 0.004259795830723663,\n 0.004645340292979379,\n 0.005065779510355506,\n 0.005524271728019902,\n 0.0060242610367497685,\n 0.006569503244169644,\n 0.007164094087536493,\n 0.0078125,\n 0.008519591661447326,\n 0.009290680585958758,\n 0.010131559020711013,\n 0.011048543456039804,\n 0.012048522073499537,\n 0.01703918332289465,\n 14.672064691274738\n ],\n [\n 0.0044444444444444444,\n 0.017777777777777778,\n 0.0044444444444444444,\n 0.035555555555555556,\n 0.017777777777777778,\n 0.013333333333333332,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.017777777777777778,\n 0.03111111111111111,\n 0.013333333333333332,\n 0.05333333333333334,\n 0.035555555555555556,\n 0.022222222222222223,\n 0.022222222222222223,\n 0.022222222222222223,\n 0.017777777777777778,\n 0.013333333333333332,\n 0.013333333333333332,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.06666666666666667,\n 0.008888888888888889,\n 0.017777777777777778,\n 0.008888888888888889,\n 0.04,\n 0.0044444444444444444,\n 0.013333333333333332,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.013333333333333332,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.026666666666666665,\n 0.035555555555555556,\n 0.008888888888888889,\n 0.044444444444444446,\n 0.035555555555555556,\n 0.026666666666666665,\n 0.022222222222222223,\n 0.022222222222222223,\n 0.013333333333333332,\n 0.008888888888888889,\n 0.017777777777777778,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.06666666666666667,\n 0.008888888888888889,\n 0.017777777777777778,\n 0.017777777777777778,\n 0.04,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.017777777777777778,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.022222222222222223,\n 0.03111111111111111,\n 0.008888888888888889,\n 0.044444444444444446,\n 0.035555555555555556,\n 0.026666666666666665,\n 0.026666666666666665,\n 0.022222222222222223,\n 0.017777777777777778,\n 0.008888888888888889,\n 0.017777777777777778,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.06666666666666667,\n 0.008888888888888889,\n 0.017777777777777778,\n 0.0044444444444444444,\n 0.017777777777777778,\n 0.03111111111111111,\n 0.008888888888888889,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.013333333333333332,\n 0.008888888888888889,\n 0.008888888888888889,\n 0.008888888888888889,\n 0.022222222222222223,\n 0.013333333333333332,\n 0.008888888888888889,\n 0.044444444444444446,\n 0.04,\n 0.022222222222222223,\n 0.022222222222222223,\n 0.022222222222222223,\n 0.03111111111111111,\n 0.0044444444444444444,\n 0.013333333333333332,\n 0.017777777777777778,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.06666666666666667,\n 0.008888888888888889,\n 0.026666666666666665,\n 0.0044444444444444444,\n 0.013333333333333332,\n 0.022222222222222223,\n 0.008888888888888889,\n 0.013333333333333332,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.008888888888888889,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.008888888888888889,\n 0.026666666666666665,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.035555555555555556,\n 0.057777777777777775,\n 0.026666666666666665,\n 0.03111111111111111,\n 0.03111111111111111,\n 0.022222222222222223,\n 0.0044444444444444444,\n 0.013333333333333332,\n 0.013333333333333332,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.06666666666666667,\n 0.0044444444444444444,\n 0.026666666666666665,\n 0.0044444444444444444,\n 0.022222222222222223,\n 0.022222222222222223,\n 0.0044444444444444444,\n 0.013333333333333332,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.013333333333333332,\n 0.017777777777777778,\n 0.013333333333333332,\n 0.008888888888888889,\n 0.035555555555555556,\n 0.05333333333333333,\n 0.022222222222222223,\n 0.026666666666666665,\n 0.035555555555555556,\n 0.022222222222222223,\n 0.008888888888888889,\n 0.02222222222222222,\n 0.008888888888888889,\n 0.0044444444444444444,\n 0.0044444444444444444,\n 0.06666666666666667\n ],\n [\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0,\n 0\n ]\n ]\n }\n }\n]", "refId": "A", @@ -896,11 +874,7 @@ ], "refresh": "", "schemaVersion": 39, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-piechart/panel_test_piechart.json b/devenv/dev-dashboards/panel-piechart/panel_test_piechart.json index e076384f273..4333993ea8e 100644 --- a/devenv/dev-dashboards/panel-piechart/panel_test_piechart.json +++ b/devenv/dev-dashboards/panel-piechart/panel_test_piechart.json @@ -53,9 +53,7 @@ }, "id": 2, "options": { - "displayLabels": [ - "name" - ], + "displayLabels": ["name"], "legend": { "values": [], "displayMode": "list", @@ -63,9 +61,7 @@ }, "pieType": "donut", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -122,22 +118,15 @@ }, "id": 11, "options": { - "displayLabels": [ - "name", - "percent" - ], + "displayLabels": ["name", "percent"], "legend": { - "values": [ - "percent" - ], + "values": ["percent"], "displayMode": "list", "placement": "right" }, "pieType": "pie", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -192,9 +181,7 @@ }, "id": 8, "options": { - "displayLabels": [ - "name" - ], + "displayLabels": ["name"], "legend": { "values": [], "displayMode": "hidden", @@ -202,9 +189,7 @@ }, "pieType": "pie", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -259,21 +244,15 @@ }, "id": 3, "options": { - "displayLabels": [ - "percent" - ], + "displayLabels": ["percent"], "legend": { - "values": [ - "percent" - ], + "values": ["percent"], "displayMode": "table", "placement": "bottom" }, "pieType": "pie", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -328,21 +307,15 @@ }, "id": 9, "options": { - "displayLabels": [ - "value" - ], + "displayLabels": ["value"], "legend": { - "values": [ - "value" - ], + "values": ["value"], "displayMode": "table", "placement": "bottom" }, "pieType": "pie", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -397,9 +370,7 @@ }, "id": 6, "options": { - "displayLabels": [ - "name" - ], + "displayLabels": ["name"], "legend": { "values": [], "displayMode": "list", @@ -407,9 +378,7 @@ }, "pieType": "pie", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -472,9 +441,7 @@ }, "pieType": "pie", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -498,10 +465,7 @@ } ], "schemaVersion": 27, - "tags": [ - "gdev", - "panel-tests" - ], + "tags": ["gdev", "panel-tests"], "templating": { "list": [] }, @@ -514,4 +478,4 @@ "title": "Panel Tests - Pie chart", "uid": "lVE-2YFMz", "version": 9 -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-polystat/polystat_test.json b/devenv/dev-dashboards/panel-polystat/polystat_test.json index faa84463019..1473fc1e7da 100644 --- a/devenv/dev-dashboards/panel-polystat/polystat_test.json +++ b/devenv/dev-dashboards/panel-polystat/polystat_test.json @@ -30,7 +30,9 @@ ], "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], "d3DivId": "d3_svg_4", - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": 2, "displayModes": [ { @@ -43,48 +45,8 @@ } ], "fontSizes": [ - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 22, - 24, - 26, - 28, - 30, - 32, - 34, - 36, - 38, - 40, - 42, - 44, - 46, - 48, - 50, - 52, - 54, - 56, - 58, - 60, - 62, - 64, - 66, - 68, - 70 + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, + 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70 ], "fontTypes": [ "Open Sans", @@ -449,7 +411,7 @@ "text": "Malaysian Ringgit (RM)", "value": "currencyMYR" }, - { + { "text": "CFP franc (XPF)", "value": "currencyXPF" }, @@ -1188,7 +1150,9 @@ ], "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], "d3DivId": "d3_svg_5", - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": 2, "displayModes": [ { @@ -1201,48 +1165,8 @@ } ], "fontSizes": [ - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 22, - 24, - 26, - 28, - 30, - 32, - 34, - 36, - 38, - 40, - 42, - 44, - 46, - 48, - 50, - 52, - 54, - 56, - 58, - 60, - 62, - 64, - 66, - 68, - 70 + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, + 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70 ], "fontTypes": [ "Open Sans", @@ -2307,7 +2231,9 @@ ], "colors": ["#299c46", "rgba(237, 129, 40, 0.89)", "#d44a3a"], "d3DivId": "d3_svg_2", - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": 2, "displayModes": [ { @@ -2320,48 +2246,8 @@ } ], "fontSizes": [ - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 22, - 24, - 26, - 28, - 30, - 32, - 34, - 36, - 38, - 40, - 42, - 44, - 46, - 48, - 50, - 52, - 54, - 56, - 58, - 60, - 62, - 64, - 66, - 68, - 70 + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, + 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70 ], "fontTypes": [ "Open Sans", diff --git a/devenv/dev-dashboards/panel-stat/panel-stat-tests.json b/devenv/dev-dashboards/panel-stat/panel-stat-tests.json index 0cbecd3b690..6799fff4bc5 100644 --- a/devenv/dev-dashboards/panel-stat/panel-stat-tests.json +++ b/devenv/dev-dashboards/panel-stat/panel-stat-tests.json @@ -36,8 +36,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -89,9 +88,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -107,8 +104,7 @@ { "alias": "__house_locations", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "labels": "", "min": 0, @@ -123,8 +119,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -176,9 +171,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -194,8 +187,7 @@ { "alias": "__house_locations", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "min": 0, "noise": 10, @@ -210,8 +202,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -263,9 +254,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -281,8 +270,7 @@ { "alias": "__house_locations", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "min": 0, "noise": 10, @@ -297,8 +285,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -350,9 +337,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -368,8 +353,7 @@ { "alias": "__server_names", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "min": 0, "refId": "A", @@ -383,8 +367,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -436,9 +419,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -453,56 +434,49 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "E", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "G", "scenarioId": "random_walk" @@ -513,8 +487,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -566,9 +539,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -583,56 +554,49 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "E", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "G", "scenarioId": "random_walk" @@ -643,8 +607,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -698,9 +661,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -716,8 +677,7 @@ { "alias": "__server_names", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "max": 200, "min": 0, @@ -734,8 +694,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -789,9 +748,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -807,8 +764,7 @@ { "alias": "__server_names", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "max": 200, "min": 0, @@ -825,8 +781,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -880,9 +835,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -898,8 +851,7 @@ { "alias": "__server_names", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "max": 200, "min": 0, @@ -929,8 +881,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -982,9 +933,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1000,8 +949,7 @@ { "alias": "__house_locations", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "labels": "", "min": 0, @@ -1016,8 +964,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1069,9 +1016,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1087,8 +1032,7 @@ { "alias": "__house_locations", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "min": 0, "noise": 10, @@ -1103,8 +1047,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1156,9 +1099,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1174,8 +1115,7 @@ { "alias": "__house_locations", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "min": 0, "noise": 10, @@ -1190,8 +1130,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1243,9 +1182,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1261,8 +1198,7 @@ { "alias": "__server_names", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "min": 0, "refId": "A", @@ -1276,8 +1212,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1329,9 +1264,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1346,56 +1279,49 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "E", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "G", "scenarioId": "random_walk" @@ -1406,8 +1332,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1459,9 +1384,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1476,56 +1399,49 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "E", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "random_walk" }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "G", "scenarioId": "random_walk" @@ -1536,8 +1452,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1591,9 +1506,7 @@ "orientation": "horizontal", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1609,8 +1522,7 @@ { "alias": "__server_names", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "max": 200, "min": 0, @@ -1627,8 +1539,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1682,9 +1593,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1700,8 +1609,7 @@ { "alias": "__server_names", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "max": 200, "min": 0, @@ -1718,8 +1626,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1773,9 +1680,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "mean" - ], + "calcs": ["mean"], "fields": "", "values": false }, @@ -1791,8 +1696,7 @@ { "alias": "__server_names", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "max": 200, "min": 0, @@ -1809,8 +1713,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1848,9 +1751,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -1863,8 +1764,7 @@ { "csvContent": "time, value\n2023-12-13T00:00:00Z, 0\n2023-12-13T00:00:00Z, 100", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -1875,9 +1775,7 @@ }, { "datasource": { - "default": true, - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1915,9 +1813,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -1930,8 +1826,7 @@ { "csvContent": "time, value\n2023-12-13T00:00:00Z, 50\n2023-12-13T00:00:00Z, 100\n2023-12-13T00:00:00Z, 50", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -1942,8 +1837,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1981,9 +1875,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -1996,8 +1888,7 @@ { "csvContent": "time, value\n2023-12-13T00:00:00Z, 0\n2023-12-13T00:00:00Z, 0", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -2008,8 +1899,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2047,9 +1937,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": true }, @@ -2062,8 +1950,7 @@ { "csvContent": "Name, value\nName1, 10\nName2, 20", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -2075,11 +1962,7 @@ ], "refresh": "", "schemaVersion": 39, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, @@ -2088,22 +1971,11 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - Stat", "uid": "EJ8_d9jZk", "version": 15, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-table/table_kitchen_sink.json b/devenv/dev-dashboards/panel-table/table_kitchen_sink.json index c92924966ce..9c05634b066 100644 --- a/devenv/dev-dashboards/panel-table/table_kitchen_sink.json +++ b/devenv/dev-dashboards/panel-table/table_kitchen_sink.json @@ -23,8 +23,7 @@ "panels": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -355,13 +354,8 @@ "footer": { "countRows": false, "enablePagination": false, - "fields": [ - "Min", - "Max" - ], - "reducer": [ - "max" - ], + "fields": ["Min", "Max"], + "reducer": ["max"], "show": true }, "frameIndex": 0, @@ -372,8 +366,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk_table" @@ -381,8 +374,7 @@ { "csvContent": "Info,Image,Pills,Data Link,Long Text\ndown,https://placecats.com/millie/200/400,hello,https://grafana.com,\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tempus et augue et lacinia. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec eu pretium tortor. Cras venenatis sapien sed mauris gravida, ut scelerisque est fringilla. Cras lorem diam, facilisis nec malesuada in, vulputate vel enim. Etiam fringilla nisi quis felis blandit tincidunt. Cras id lacus ornare, ullamcorper nisl eget, bibendum odio. Pellentesque imperdiet, leo a imperdiet venenatis, ligula risus venenatis quam, vel euismod magna nisi sit amet leo.\"\nup,https://placecats.com/neo/200/400,\"[1,2,3,\"\"foo\"\",\"\"bar\"\"]\",https://grafana.com/solutions/kubernetes/,\"Sed imperdiet eget diam sit amet fringilla. Curabitur quis lacus blandit, mollis diam non, accumsan tortor. Aliquam ac tellus eget dui facilisis tempor eu id nulla. Maecenas ultrices turpis eu elementum imperdiet. Fusce eget rhoncus mi, et egestas lectus. Mauris facilisis auctor enim sed malesuada. Maecenas placerat ultricies metus vitae viverra. In hac habitasse platea dictumst. Mauris ipsum nisl, dictum eu aliquam eleifend, rutrum id orci. Nullam eget dui et odio eleifend porttitor.\"\nup fast,https://placecats.com/bella/200/400,\"foo,1,4,beep\",https://k6.io/,\"Proin ac libero vulputate ex vulputate pharetra ut vel lacus. Phasellus quis dolor sed leo finibus scelerisque. Ut vel finibus leo, sed viverra ipsum. Suspendisse vitae rutrum arcu. Donec sed tellus vel lectus bibendum vestibulum. Sed eu felis non velit dictum pulvinar eu et leo. Aenean et dignissim arcu. Nam luctus at neque quis efficitur. Fusce tempus at nibh a imperdiet. Nullam malesuada ac magna at facilisis. Duis pretium aliquam eros. Donec pharetra dignissim dolor non bibendum. Ut gravida mi id urna tempus, at ullamcorper felis vulputate. Duis congue augue ex, sed finibus leo ornare ut. Mauris non quam sodales, dignissim lorem eget, tincidunt mauris. Aliquam ut velit auctor, vestibulum metus sed, mollis massa.\"\ndown fast,https://placecats.com/neo_2/200/400,\"foo,bar,baz,a longer one,bim\",https://grafana.com/products/cloud/,\"Nullam in pulvinar justo. Nunc dictum arcu ac pellentesque bibendum. Sed in erat turpis. Vestibulum eu orci ac ligula lobortis tempus. Fusce consectetur feugiat magna, eu tempor nibh vestibulum ac. Aliquam erat volutpat. Vivamus sit amet viverra enim. Quisque mollis odio nulla, nec vulputate sem placerat in. Etiam dolor sapien, pulvinar in accumsan at, consequat eget nisi. Nunc condimentum neque magna, congue consectetur dui efficitur interdum. Nam lobortis fringilla maximus. Vestibulum eu dui a velit condimentum eleifend consequat nec lectus.\"", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_content" @@ -482,7 +474,6 @@ } ], "datasource": { - "uid": "PD8C576611E62080A", "type": "grafana-testdata-datasource" }, "options": { @@ -490,9 +481,7 @@ "cellHeight": "sm", "footer": { "show": false, - "reducer": [ - "sum" - ], + "reducer": ["sum"], "countRows": false, "fields": "" } @@ -507,8 +496,7 @@ { "baseFilters": [], "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "filters": [], "name": "Filters", diff --git a/devenv/dev-dashboards/panel-table/table_markdown.json b/devenv/dev-dashboards/panel-table/table_markdown.json new file mode 100644 index 00000000000..863edd09fed --- /dev/null +++ b/devenv/dev-dashboards/panel-table/table_markdown.json @@ -0,0 +1,124 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1735, + "links": [], + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "gdev-testdata" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false, + "wrapHeaderText": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "markdown" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "type": "markdown", + "dynamicHeight": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 18, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "cellHeight": "lg", + "footer": { + "enablePagination": true, + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "12.1.0-pre", + "targets": [ + { + "csvContent": "id,markdown\n1,\"
\n
Definition list
\n
Is something people use sometimes.
\n
Markdown in HTML
\n
Does *not* work **very** well. Use HTML tags.
\n
\"\n2,\"Three or more...\n\n---\n\nHyphens\n\n***\n\nAsterisks\n\n___\n\nUnderscores\"\n3,\"Here's a line for us to start with.\n\nThis line is separated from the one above by two newlines, so it will be a *separate paragraph*.\n\nThis line is also a separate paragraph, but...\nThis line is only separated by a single newline, so it's a separate line in the *same paragraph*.\"\n4,\"red, green, blue\"\n5,\"\"\n6,\"[Link](https://grafana.com), or HTML link\"\n7,\"1. foo\n1. bar\n - baz\n * bim\n3. bip\"\n8,\"# heading 1\n## heading 2\n### heading 3\n#### heading 4\n##### heading 5\n###### heading 6\"\n9,\"Emphasis, aka italics, with *asterisks* or _underscores_.\n\nStrong emphasis, aka bold, with **asterisks** or __underscores__.\n\nCombined emphasis with **asterisks and _underscores_**.\n\nStrikethrough uses two tildes. ~~Scratch this.~~\n\nunderline does require an HTML element tho.\"\n10,\"```javascript\nvar s = 'JavaScript syntax highlighting';\nalert(s);\n```\"\n11,\"\n| Month | Savings |\n| -------- | ------- |\n| I heard | $250 |\n| you like | $80 |\n| tables | $365 |\"\n", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "gdev-testdata" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Markdown Table", + "type": "table" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Panel Tests - Table - Markdown", + "uid": "2769f5d8-0094-4ac4-a4f0-f68f620339cc", + "version": 1 +} diff --git a/devenv/dev-dashboards/panel-table/table_pagination.json b/devenv/dev-dashboards/panel-table/table_pagination.json index 48f5df03f1f..cafa00c36d5 100644 --- a/devenv/dev-dashboards/panel-table/table_pagination.json +++ b/devenv/dev-dashboards/panel-table/table_pagination.json @@ -1,2683 +1,2661 @@ { - "annotations": { - "list": [ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1110, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [ { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" + "options": { + "rent": { + "color": "yellow", + "index": 0 + } + }, + "type": "value" } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "property_type" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "store": { + "color": "blue", + "index": 0 + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "title" + }, + "properties": [ + { + "id": "custom.width", + "value": 205 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Number of rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 66 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.inspect", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "URL" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Show details", + "url": "${__value.raw}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Surface" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background-solid" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "orange", + "value": 100 + }, + { + "color": "yellow", + "value": 200 + }, + { + "color": "green", + "value": 300 + }, + { + "color": "#EAB839", + "value": 500 + }, + { + "color": "#6ED0E0", + "value": 1000 + }, + { + "color": "#EF843C", + "value": 2000 + } + ] + } + }, + { + "id": "unit", + "value": "areaM2" + }, + { + "id": "custom.width", + "value": 93 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Type" + }, + "properties": [ + { + "id": "custom.width", + "value": 94 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "City" + }, + "properties": [ + { + "id": "custom.width", + "value": 120 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 77 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Price" + }, + "properties": [ + { + "id": "unit", + "value": "currencyUSD" + } + ] + } ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 1110, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [ - { - "options": { - "rent": { - "color": "yellow", - "index": 0 - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "property_type" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background" - }, - { - "id": "mappings", - "value": [ - { - "options": { - "store": { - "color": "blue", - "index": 0 - } - }, - "type": "value" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "title" - }, - "properties": [ - { - "id": "custom.width", - "value": 205 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Number of rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 66 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Description" - }, - "properties": [ - { - "id": "custom.inspect", - "value": true - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "URL" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "Show details", - "url": "${__value.raw}" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Surface" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background-solid" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "dark-red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "yellow", - "value": 200 - }, - { - "color": "green", - "value": 300 - }, - { - "color": "#EAB839", - "value": 500 - }, - { - "color": "#6ED0E0", - "value": 1000 - }, - { - "color": "#EF843C", - "value": 2000 - } - ] - } - }, - { - "id": "unit", - "value": "areaM2" - }, - { - "id": "custom.width", - "value": 93 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Type" - }, - "properties": [ - { - "id": "custom.width", - "value": 94 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "City" - }, - "properties": [ - { - "id": "custom.width", - "value": 120 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 77 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Price" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 0 - }, - "id": 2, - "options": { - "footer": { - "enablePagination": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Surface" - } - ] - }, - "pluginVersion": "9.0.0-pre", - "targets": [ - { - "csvContent": "\"id\",\"created_on\",\"operation\",\"property_type\",\"place_name\",\"place_with_parent_names\",\"country_name\",\"state_name\",\"geonames_id\",\"lat_lon\",\"lat\",\"lon\",\"price\",\"currency\",\"price_aprox_local_currency\",\"price_aprox_usd\",\"surface_total_in_m2\",\"surface_covered_in_m2\",\"price_usd_per_m2\",\"price_per_m2\",\"floor\",\"rooms\",\"expenses\",\"properati_url\",\"description\",\"title\",\"image_thumbnail\"\nf73c373e0588bf568164a057dd94203ac87d47b1,2014-08-07,rent,store,Catu,|Brasil|Bahia|Catu|,Brasil,Bahia,,,,,0,,0,0,,,,,,,,http://www.properati.com.br/mfye_alugar_outros_catu,\"Otimo Imovel com o melhor valor da regiao, confira as fotos - \",Exclusividade! - Loja Em Catu,https://thumbs4.properati.com/3/jA8isZw8Xju7z3-V2I-ED8x5D2o=/trim/198x0/smart/filters:strip_icc()/galeriadefotos.net.br/backup/upload/15989/1598966287bc7/zd533ba5e829b0543093a45e1d8398bd7.jpg\nd6d377927ab49671acdd38aa6f661a460601a88b,2015-01-20,rent,apartment,Pernambuco,|Brasil|Pernambuco|,Brasil,Pernambuco,,\"-8.126271,-34.903793\",-8.126271,-34.903793,1200,BRL,1184.32,383.55,,63,6.088095238095239,19.047619047619047,1,2,,http://www.properati.com.br/w71k_alugar_apartamentos_pernambuco,\"CONTATOS: Claro: 81./ Tim: 81./ Oi: 81./ Vivo: 81.Apartamento de 2 quartos mais Dependência completa de empregada, 1.º andar de edifício localizado na Rua Dr. Gaspar Regueira da Costa, 99, Boa Viagem.Este apartamento é constituído por: 1 Sala, 2 Quartos, 2 Banheiros, Cozinha, Área de serviço, Dependência completa de empregada e 1 lugar de estacionamento.O Valor do aluguer já inclui taxas.Agende já a sua visita.Nota: O Edifício irá ser pintado exteriormente, em breve.ISI-120-T2 #ref:ISI-120-T2 - ISI-120-T2 - idalécio Soares\",\"Apartamento de 2 quartos, DCE, 1.º Andar, 63 m2, Boa Viagem - Recife\",https://thumbs4.properati.com/0/B9BCwP1HK7NvZIYINGkFNnNfOZ4=/trim/198x0/smart/filters:strip_icc()/feedmedia.egorealestate.com/Zfeed/S5/C4123/P2270979/Tphoto/ID03a72200-0000-0500-0000-00000141a7a5.JPG\nc6826a9508436920fd59b46a202354f3e4823d22,2014-10-29,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.1546253,-45.7906242\",-23.1546253,-45.7906242,0,BRL,0,0,,3800,,,,,,http://www.properati.com.br/s41f_alugar_comercial_cacapava,\"Condomínio com 22 Módulos de Galpões; 3.800m2 a 12.600m2; 12m pé-direito médio; Piso 6 TON/m2; Ã�rea total de Terreno : 324.000 m2; Ã�rea Total construída: 133.200 m2; 293 vagas para automóveis; 103 vagas para caminhões; Refeitório para 256 pessoas por turno;Benefícios Fiscais no município de Caçapava conforme Lei Lei 5055/211; Isenção de Taxas;Isenção de IPTU; Redução de ISS; Reversão de ICMS.Valores locação = R$ 15,00 o m2Tx Cond. = R$ 1,50 o m2Valores poderão ser alterados sem prévio aviso.A Heron Imóveis oferece este(a) Comercial em Caçapava, no bairro Caçapava. Gostou desse(a) Comercial em Caçapava? Então veja mais informações! Acesse o nosso site clicando no botão Contate o anunciante ou ligue - ou ainda entre em contato conosco pelo email contato@ Obrigado!\",\"Comercial em Caçapava, no bairro Caçapava\",https://thumbs4.properati.com/1/J2n8nXVlvduy6EJjyLwPneEoc28=/trim/198x0/smart/filters:strip_icc()/www.heronimoveis.com.br/fotos/F00/35/23500-DSC06177.JPG\nba957ad3cb2d12d55d1f0466eeffe788a35a6f4f,2014-08-04,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7780086,-49.3450562\",-20.7780086,-49.3450562,950,BRL,751.1,243.25,,,,,,1,,http://www.properati.com.br/lxp9_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95876 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Garden Village II..:Casa Condomnio Garden Village II, Casa Condominio, Dormitrios: 03, Tipo Apto.: 01, Wc. Social: 01, Sala de Visita: 01, Copa: 01, Cozinha: 01, Cozinha Planejada, Despensa, rea de Servio, Ar Condicionado, Alarme, Acabamento Em Gesso, Sanca, Campainha, Cerca Eletrica, Forro de Laje, Piso Dormitrios: Frio, Piso Salas: Frio, M2/ Terreno: 145, M2 rea Construida: 70, Garagem Descoberta: 01, Salo de Festa, Detalhes: R$ 950,00 + Iptu + Condomnio\", Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Garden Village II,https://thumbs4.properati.com/9/xcrfHjSnXdhGcOK1m1wg1jHJlsw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/803/8860a8e8-6d62-49e6-a992-54ab8f360f35.jpg\n0999b33908c71af3b8be282ab35ab61b38a46353,2014-07-16,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,4300,BRL,4078.98,1321,,319,4.141065830721003,13.47962382445141,,4,,http://www.properati.com.br/ifuj_alugar_apartamentos_vila-olimpia,\"Apartamento na Vila Olí­mpia, com 4 quartos, sendo 2 suí­tes,Ampla sala com terraí§o, piso de madeira corrida, 1 dos quartos pode ser revertido para sala de tv ou escritório.Quartos com carpete de madeira, as suí­tes com armí¡rios embutidos, uma delas com ar condicionado.Armí¡rio corredor.Cozinha com armí¡rio, Piso de granito, na cozinha e lavanderia.  Referíªncia: AP34\",\"Apartamento no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/8/nxrbzTDipvKnqdx1wDABEyIbSes=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/34/c789b808-bf30-460d-9376-16299ce5abbb.jpg\n011d5618639ce229b012022a32b22eff9abd213e,2014-11-19,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.9648026,-46.3855723\",-23.9648026,-46.3855723,1300,BRL,1323.12,428.5,,60,7.141666666666667,21.666666666666668,4,1,,http://www.properati.com.br/tl4s_alugar_apartamentos_sao-paulo,\"PACOTE R$ 1.300,00 OTIMO APARTAMENTO MOBILIADO COM TERRAÇO PROXIMO SHOPPING , PRAIA E COMERCIOS ACEITA FIADOR / SEG. FIANÇA A 3 Milenio oferece este(a) Apartamento em Sao Vicente, no bairro Gonzaguinha - 1 dormitórios. Gostou desse(a) Apartamento em Sao Vicente? Então veja mais informações! Acesse o nosso site clicando no botão Contate o anunciante ou ligue - ou ainda entre em contato conosco pelo email vendas@ Obrigado!\",\"Apartamento em Sao Vicente, no bairro Gonzaguinha - 1 dormitórios\",https://thumbs4.properati.com/4/x8BqTbd304ntTBY5pTf5Yi_nNMw=/trim/198x0/smart/filters:strip_icc()/www.imobiliariasdesantos.com.br/fotos/F00/74/757400-FOTOS%20HELANE%20001.jpg\n6f274a2d23ffa89cb0301f3277585beda7723ce8,2014-09-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8343086,-49.3805559\",-20.8343086,-49.3805559,5000,BRL,3953.34,1280.31,,,,,,,,http://www.properati.com.br/o23s_alugar_comercial_sao-jose-do-rio-preto_rua-nuno-alvares-pereira,\"Ref.: SA95668Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Pq. EstorilM2 rea Construida: 170, Detalhes: Salo de 170 M, Com Mezanino. J Com Acessibilidade. Tem Uma Linda Fachada e Estacionamento Para Clientes.\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Pq. Estoril,https://thumbs4.properati.com/8/7VqZVGV6Hl3paOrhYXv_ksowyEw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1875/TG5yXGj9EJPaa5o0dWgL.jpg\ndb6f890847bcacc6ec3e78a0e6613132174e4c70,2014-12-05,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178411,-49.387605\",-20.8178411,-49.387605,10000,BRL,7906.68,2560.62,,,,,,1,,http://www.properati.com.br/ue07_alugar_casas_sao-jose-do-rio-preto_rua-cila,\"Ref.: CO95026Tipo: Casa Comercial, Predio ComercialCidade: So Jos do Rio Preto - SPBairro: RedentoraSobrado, Dormitrios: 4, Tipo Apto.: 4, C/ A/e: 4, A/e C/ Interior: 4, Wc. Social: 1, Copa/cozinha: 1, Cozinha C/ A/e: 1, Despensa, Salas : 1, Sala P/ Ambientes : 4, M2 rea Construida: 330, Entrada Lateral: 1, Piso Dormitrios: Frio\",\"Ref.: CO95026 Tipo: Casa Comercial, Predio Comercial Cidade: So Jos do Rio Preto - SP Bairro: Redentora\",https://thumbs4.properati.com/2/DNREjVLgpGPzTlv8aIvhpd7KZkI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2461/02ea33d3-3ebd-4139-a380-b60eb84c87d1.jpg\nf7d3243419c85cf439189481819f13cb22ab0498,2014-10-01,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8126181,-49.3404534\",-20.8126181,-49.3404534,3800,BRL,3004.52,973.03,,,,,,,,http://www.properati.com.br/qz8t_alugar_casas_sao-jose-do-rio-preto_avenida-miguel-damha,\"Ref.: CA95902Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha III..: Casa Condomnio Damha III, Casa Condominio, Dormitrios: 03, Suite: 01, Closet , Tipo Apto.: 02, C/ A/e: 02, Home, Copa/cozinha: 01, Cozinha Planejada, Piscina, Varanda, rea de Servio, Wc. de Empregada/servio, Forro de Laje, M2/ Terreno: 450, M2 rea Construida: 240, rea de Lazer, Garagem Descoberta: 02, Garagem Coberta: 02, Churrasqueira, Valor Condominio R$-: 420,00\",Ref.: CA95902 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha III,https://thumbs4.properati.com/8/cqu5q3tBJOddQ526fWf2puVwINI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2060/6064cf7e-f8ed-44f3-8a80-b6576de27d62.jpg\ncbfc4c495b3578c995363825a85a821217fab4ec,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,14000,BRL,11069.36,3584.87,,,,,,,,http://www.properati.com.br/ri3p_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95918Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Mini Dist. Industrial Adail Vetorasso..: Salo Comercial No Mini Distrito Industrial Adail Vetorasso, Salo Comercial, Wcs.: 02, Cozinha: 01, Escritrio, M2/ Terreno: 2.100, M2 rea Construida: 1.350, Detalhes: Cobertura Metlica.p Direito Com 7 Metros.corredor Lateral.\",Ref.: SA95918 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Mini Dist. Industrial Adail Vetorasso,https://thumbs4.properati.com/9/INPTa4YGuWfsekPfC8ez-rqd3PI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2285/Mhd9hMU2ZwU9YXAC9Hzi.jpg\nb4dab15f9d4411d0847d1871e0a62e107b492fdc,2014-08-02,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.8773135,-46.7592613\",-23.8773135,-46.7592613,28000,BRL,22138.72,7169.74,,,,,,,,http://www.properati.com.br/lud3_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95869Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Eng. Schimidt..: Salo Comercial Em Engenheiro Schimidt, Salo Comercial, Obs: Salo Ideal Para Transportadora Com Frente Para 03 Marginal, Esquina Com Rua Projetada 01 e Rodovia W. Luiz, M2/ Terreno: 5.500, M2 rea Construida: 2.200\",Ref.: SA95869 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Eng. Schimidt,https://thumbs4.properati.com/3/oJm3M_UtBn2PVRsZ2O4n48IvhuA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/780/60d090bc-4a05-4929-88ae-aca5b9873a76.jpg\nd3769eb1b41c780c83f2d176adae8ad9a81b7ba3,2014-10-08,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.818728,-49.4212608\",-20.818728,-49.4212608,16000,BRL,12650.68,4096.99,,,,,,,,http://www.properati.com.br/r6d9_alugar_comercial_sao-jose-do-rio-preto_avenida-joao-batista-vetorasso,\"Ref.: SA95810Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Waldemar VerdiM2/ Terreno: 5.000, M2 rea Construida: 1.400, Detalhes: Com Recepo e 8 Salas. P Direito Com 7 M.\",Salo,https://thumbs4.properati.com/6/GFkI3LbwZQAhB8USv5fTltHxz0E=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2179/QEWBOD1T8GCOjBi0H6WI.jpg\nf51aebdb554e9b5dfa16c3538d3376040f5f2b5c,2014-08-20,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,16000,BRL,12650.68,4096.99,,,,,,,,http://www.properati.com.br/np97_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95723Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel HaddadM2 rea Construida: 322, Obs: Salo Ideal Para Loja, 217m Terreo e 105m Mezanino. Estacionamento Na Frente.imvel Novo.\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad,https://thumbs4.properati.com/2/viy2bV22HhxBkY_449PiH9sLQPw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1548/9242514f-75f0-4bdc-a5b1-68a958cbda17.jpg\n7266f270625a52c84adaf69ac08a53b397617247,2014-09-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,6000,BRL,4744,1536.37,,,,,,,,http://www.properati.com.br/o20x_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95742Tipo: Salo IndustrialCidade: So Jos do Rio Preto - SPBairro: Quinta das PaineirasWcs.: 02, Cozinha: 01, M2/ Terreno: 720, M2 rea Construida: 458,90, Obs: Salo Industrial Grande Com 458m, Com Boa Acessibilidade e tima Localizao, 02 Quadras da Avenida Potirendaba, Fcil Acesso e Entrada Para Caminhes.\",Tipo: Salo Industrial Cidade: So Jos do Rio Preto - SP Bairro: Quinta das Paineiras,https://thumbs4.properati.com/3/0rnMsn1G6rWTjCQ9lnrLn6WR7JA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1771/KET9NdQ0WNrtQlhGEVEq.jpg\nc1e10a8afe111ba5caa5034df1ecb56b25b772f9,2014-08-19,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,12000,BRL,9488,3072.74,,,,,,,,http://www.properati.com.br/muvz_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95766Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel HaddadWc. Social: 04, M2/ Terreno: 980, M2 rea Construida: 500, Obs: Entrada Lateral Com Acesso Para Caminho, Com 04 Wcs. Sendo 02 Wc P/ Deficiente.\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad,https://thumbs4.properati.com/3/GvRImi73Q9koPTqVulTG5pg1J2s=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1519/T181YBWZd62P9tMfgs1F.jpg\nd156f7cc95141ef1fca4de81d6dc65b143c9988f,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,12000,BRL,9488,3072.74,,,,,,,,http://www.properati.com.br/muwd_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95749Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel HaddadM2 rea Construida: 280, Obs: Salo Ideal Para Loja, 217mterreo + 68m Mezanino.estacionamento Na Frente,\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad,https://thumbs4.properati.com/5/A5TB5m6yLugDjIdQVsXy9br70YQ=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1533/eeec7b18-daa6-422b-aac1-82894f366e60.jpg\n90ed612641d0db18967ee9c07edc8b0647c97976,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,12000,BRL,9488,3072.74,,,,,,,,http://www.properati.com.br/muwc_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95725Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel HaddadM2 rea Construida: 280, Obs: Salo Ideal Para Loja, 217m Terreo, + 68m Mezanino.estacionamento Na Frente, Imvel Novo\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad,https://thumbs4.properati.com/2/8rbyOC1iGXRWRZqh5Z6pcOsdsfQ=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1532/a142f7a0-ff97-4794-84d8-4144253429c3.jpg\nc69e1ca4e7cd3346bc510240893bf2c2c7437e8e,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,12000,BRL,9488,3072.74,,,,,,,,http://www.properati.com.br/muvh_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95724Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel HaddadM2/ Terreno: 280, Obs: Salo Ideal Para Loja, 217m Terreo e + 68m Mezanino.Estacionamento Na Frente,imvel Novo.\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad,https://thumbs4.properati.com/1/3gyCoSFzrSme_NcQhKWIEnuoeaY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1501/CeAfrlKKrQIRE9uzNxoa.jpg\n0c5e391bac4d007585efb02466681c760a758423,2014-09-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8379345,-49.3370139\",-20.8379345,-49.3370139,8000,BRL,6325.32,2048.49,,,,,,,,http://www.properati.com.br/q4jv_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95899Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Ch. Estancia Jockey Clube..: Salo Comercial , Salo Comercial, Wcs.: 04, Wcs. C/ A/e, Cozinha: 01, Obs: P Direito 9 Metros, M2 rea Construida: 1.000, Detalhes: Salo Novo, Sendo Os Wcs Piso Porcelanato, Cozinha Porcelanato e Piso Liso, Com Telhado Vulcanizado.\",Ref.: SA95899 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Ch. Estancia Jockey Clube,https://thumbs4.properati.com/8/OM-MkRbrVgdcQa163wlgXjWDHqU=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2007/8541c9f9-4477-448d-a193-d2b00dc5eed4.jpg\n01bdf823f7827d745574bb7a180a39e4db359463,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,8000,BRL,6325.32,2048.49,,,,,,,,http://www.properati.com.br/muux_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95894Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Ch. Jockey Clube..: Salo Comercial Na Chcara Jockey Clube, Salo Comercial, M2/ Terreno: 5.000, M2 rea Construida: 1.150, Detalhes: Galpo Com Piso Cimento Polido, Estrutura Metlica, Entrada Lateral Para Caminho.p Direito Alto Com 6,/2 M. \",Ref.: SA95894 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Ch. Jockey Clube,https://thumbs4.properati.com/1/HZjM3Xdq2xWgpQ5gSrCVIASVnJA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1481/Tz9VYOVWXC1ZVU5fUHwL.jpg\n050a20b55332403b962575877b81d609a5988a15,2014-09-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8379345,-49.3370139\",-20.8379345,-49.3370139,8000,BRL,6325.32,2048.49,,,,,,,,http://www.properati.com.br/q4ju_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95900Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Ch. Estancia Jockey Clube..: Salo Comercial , Salo Comercial, Wcs.: 04, Wcs. C/ A/e, Cozinha: 01, Obs: P Direito 9 Metros, M2 rea Construida: 1.000, Detalhes: Salo Novo, Sendo Os Wcs Piso Porcelanato, Cozinha Porcelanato e Piso Liso, Com Telhado Vulcanizado.\",Ref.: SA95900 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Ch. Estancia Jockey Clube,https://thumbs4.properati.com/3/ZL10NlMLKOM5C49dt-MjpOTaaMo=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2006/9fbe5c67-00ba-4e7b-9904-08316f433dd4.jpg\nfeb178373d5eab7df3b6c546a5d1e6a192156c7f,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8457948,-49.353464\",-20.8457948,-49.353464,8000,BRL,6325.32,2048.49,,,,,,,,http://www.properati.com.br/muuv_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95892Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Ch. Jockey Clube..: Salo Comercial Na Chcara Jockey Clube, Salo Comercial, M2/ Terreno: 5.000, M2 rea Construida: 1.150, Detalhes: Galpo Com Piso Cimento Polido, Estrutura Metlica, Entrada Lateral Para Caminho.p Direito Alto Com 6,/2 M.\",Ref.: SA95892 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Ch. Jockey Clube ,https://thumbs4.properati.com/5/xwV26hopilS3ZK6xLE-O3iy4rE4=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1479/vT0mPYs7U2dAsti8LWGQ.jpg\n882a8542b44e551782d3126a1d43d907cdba854b,2014-08-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8442587,-49.3291753\",-20.8442587,-49.3291753,7000,BRL,5534.66,1792.43,,,,,,,,http://www.properati.com.br/mt5y_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95817Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Marginal Eng. SchimidtWc. Social: 02, Cozinha, Mesanino, Escritrio, M2/ Terreno: 2.000, M2 rea Construida: 1.100, Pintura Nova, Piso Dormitrios: Cimentado;, Piso Salas: Polido;, Detalhes: Salo Novo, Com Estrutura Metlica e Banheiro C/ Acessibilidade.\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Marginal Eng. Schimidt,https://thumbs4.properati.com/4/FPT3HYl7BCrZMA5Okj6iNxUgPRA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1432/MaEwnQ1cL6HLcrscPFb8.jpg\n201e0ccc9fc63a78f68391bdd74ac5407e07da9b,2014-08-04,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,1000,BRL,790.66,256.06,,,,,,1,,http://www.properati.com.br/lxpd_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95884Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Parque Liberdade III..:Casa Condomnio Parque da Liberdade III, Casa Condominio, Dormitrios: 03, C/ A/e: 02, Wc. Social: 01, Wcs. C/ A/e, Sala de Visita: 01, Cozinha: 01, Cozinha Planejada, Interfone, Acabamento Em Gesso, Sanca, Campainha, Forro de Laje, Obs: Imvel Semi Novo, Com Projeto da Arquiteta Kenia Miranda, o Imvel Tem Papel de Parede, Cortinas Nos Dormitrios e Sala, Luminrias Na Cozinha., M2/ Terreno: 143, Garagem Coberta: 02, Churrasqueira, Detalhes: R$ 1.000,00 + Iptu + Condominio\",Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Parque Liberdade III,https://thumbs4.properati.com/2/v319TrEqOc-5Hy9xmTQXh1KL2UE=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/807/7331be4a-3945-41cd-b761-4322696192f5.jpg\n05cf0728d4ba6016469b6b4a864542a1824dc25c,2014-08-15,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.7688712,-46.7100661\",-23.7688712,-46.7100661,2000,BRL,1581.32,512.12,,,,,,,,http://www.properati.com.br/mqtt_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95463Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Jardim So MarcosWc. Social: 2, Cozinha: 1, Mesanino, Recepo, M2/ Terreno: 406, M2 rea Construida: 110, Pintura Nova, Piso Salas: Piso Frio\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Jardim So Marcos,https://thumbs4.properati.com/9/EMUy3dbQiGwd8LmfHH92qsmCQ7Q=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1375/srbDQgpudCyI2lewxvjS.jpg\nf5942694fe76bfe705275acfe74148c588367f86,2014-09-01,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.7688712,-46.7100661\",-23.7688712,-46.7100661,4000,BRL,3162.64,1024.24,,,,,,8,,http://www.properati.com.br/o215_alugar_casas_sao-jose-do-rio-preto_rua-antonio-dias,\"Ref.: CO95681Tipo: Casa ComercialCidade: So Jos do Rio Preto - SPBairro: Jardim So MarcosWc. Social: 04, Sala(s): 08, M2/ Terreno: 280, Garagem Coberta: 02, Forro de Laje, Piso Salas: Frio, Iptu Valor: 58,00, Detalhes: timo Local Para Clincas.\",Tipo: Casa Comercial Cidade: So Jos do Rio Preto - SP Bairro: Jardim So Marcos,https://thumbs4.properati.com/4/F58YgAYlsu22NFL9RPPH5RURZJ8=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1779/sr0aS0NcXQGZ7HsOtyan.jpg\n1b093694ca05135b213162c0bd499e9adb708309,2014-12-10,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.6302071,-46.5818983\",-23.6302071,-46.5818983,4000,BRL,3162.64,1024.24,,,,,,,,http://www.properati.com.br/ufsq_alugar_comercial_sao-jose-do-rio-preto_avenida-marginal,\"Ref.: SA95942Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Pq. Res. Manor Daud..: Salo Comercial No Parque Residencial Manor Daud, Salo Comercial, Detalhes: Salo Com 340m, Entrada Para Caminho.frente Para Br153\",Ref.: SA95942 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Pq. Res. Manor Daud,https://thumbs4.properati.com/4/4e8bVPqS0g1ezrFKP9fcdzvSn0I=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2493/rJ3aboE1bZnwuqJHy4Lr.jpg\n50bd70c45a9a9b087c404cac6b67c491f64c211e,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2500,BRL,1976.65,640.15,,,,,,,,http://www.properati.com.br/ri3l_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95916Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Res. Caetano..: Salo Comercial No Residencial Caetano, Salo Comercial, Wcs.: 02, Cozinha: 01, M2 rea Construida: 350, Detalhes: Cozinha2 Wc Com Acessibilidade;1 Porta Na Frente1 Porta Na Lateral;barraco Novo.cobertura Com Estrutura Metlica. P Direito de 6 Metros.\",\"Ref.: SA95916 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Res. Caetano ..: Salo Comercial No Residencial Caetano, Salo Comercial, Wcs.: 02, Cozinha: 01, M2 rea Construida: 350, Detalhes: Cozinha 2 Wc Com Acessibilidade\",https://thumbs4.properati.com/9/l325GXwN3fD9xS0dglH5hmaB83A=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2281/FjKJBqBJFxhvJ4atJVuO.jpg\n50a4a96d3ec01b7b1032e5eec4a3496b38f35c4f,2014-10-09,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7780086,-49.3450562\",-20.7780086,-49.3450562,2500,BRL,1976.65,640.15,,,,,,,,http://www.properati.com.br/r9j7_alugar_casas_sao-jose-do-rio-preto_avenida-belvedere,\"Ref.: CA95904Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Village Damha Rio Preto I..: Casa Em Condomnio Village Damha Rio Preto I, Casa Condominio, Dormitrios: 03, Tipo Apto.: 01, C/ A/e: 03, Cozinha: 01, Cozinha Planejada, Varanda, rea de Servio, Wc. de Empregada/servio, Garagem Coberta: 02, Churrasqueira\",Ref.: CA95904 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Village Damha Rio Preto I,https://thumbs4.properati.com/1/CVM0xMvGlg_DX1BluR3vWpRkhMU=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2206/7f2a5d99-2465-48fb-859c-a52311ba0c18.jpg\nf5be311dcee4a66dcb26f463f76c49b6084de8ab,2014-11-11,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,1600,BRL,1411.74,457.2,,,,,2,1,,http://www.properati.com.br/t8nu_alugar_apartamentos_tatuape_rua-serra-de-braganca,\"APTO DE 40M/2 COM 01 DORMITRIO, SALA AMPLA COM SACADA, COZINHA PLANEJADA, WC SOCIAL, REA DE SERVIO, 01 VAGA DE GARAGEM LIVRE, LAZER COMPLETO COM PISCINA, PLAYGROUD, SALO DE FESTA, CHURRASQUEIRA, SAUNA, SALA DE FITINESS, SALA DE LEITURA, SALO DE JOGOS, EXCELENTE LOCALIZAO. O APTO ESTA COM FOGO, GELADEIRA, MICROONDAS, SUGGAR, BALCO ESTILO COZINHA AMERICANA, 02 BANQUETAS, 02 MESINHAS DE CANTO, 01 HOME-OFFICER, CAMA BOX COM COLCHO, VENTILADOR DE TETO, ARMRIO NO DORMITRIO.\",TOLEDO CONSULTORIA JURIDICA E IMOBILIARIA,https://thumbs4.properati.com/6/-9lTZIOGIGHwHaddpPoJu6s92hY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/2/6c5e21eb-1c7b-41d6-9257-b6e7994ea93c.jpg\na031b9b0ccce57678aab134025713cdb4ad7ed19,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7630911,-49.3810839\",-20.7630911,-49.3810839,1500,BRL,1185.99,384.09,,,,,,,,http://www.properati.com.br/ri3h_alugar_comercial_sao-jose-do-rio-preto_rua-gumercindo-tomas-de-aquino,\"Ref.: SA95914Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Centenrio da Emancipao..: Salo Comercial No Distrito Industrial Centenrio da Emancipao, Salo Comercial, Wcs.: 02, Cozinha: 01, M2/ Terreno: 200, M2 rea Construida: 100, Detalhes: Salo Em Construo, Com 2 Wc Com Acessibilidade e Cozinha.porta Frente e Lateral.estrutura Metlica, P Direito 5 Metros.\",Ref.: SA95914 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial Centenrio da Emancipao,https://thumbs4.properati.com/5/cuTILpWk120n1EtpTTP7CuHqFTI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2277/xOFbBdd0MAPLxuFIMMMc.jpg\n8198126233f30e45a7ec723640ecd09fc933f35f,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7630911,-49.3810839\",-20.7630911,-49.3810839,1500,BRL,1185.99,384.09,,,,,,,,http://www.properati.com.br/ri3j_alugar_comercial_sao-jose-do-rio-preto_rua-gumercindo-tomas-de-aquino,\"Ref.: SA95915Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Centenrio da Emancipao..: Salo Comercial No Distrito Industrial Centenrio da Emancipao, Salo Comercial, Wcs.: 02, Cozinha: 01, M2/ Terreno: 200, M2 rea Construida: 100, Detalhes: Salo Em Construo, Com 2 Wc Com Acessibilidade e Cozinha.porta Frente e Lateral.estrutura Metlica, P Direito 5 Metros.\",Ref.: SA95915 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial Centenrio da Emancipao,https://thumbs4.properati.com/0/LWklCEKjfuTI94hwh7U-tpkiQxw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2279/YBRlngu70KNBBuf3pXju.jpg\ncfe8aedbfe7496306c9ee598fdb0799d1a4d43a7,2014-08-14,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178411,-49.387605\",-20.8178411,-49.387605,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/mpsw_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95860Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: RedentoraSalo Comercial, Terrea, Acabamento Em Gesso, Forro de Laje, Piso Salas: Frio, M2 rea Construida: 160\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Redentora,https://thumbs4.properati.com/1/w-odT7rMEUYcr3TVS4fo2ausAAI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1278/Kp7YsojHwZdQDjK4JvMz.jpg\n9002dc7d32f4f097a4f9c789ad93b7f5304396cf,2014-08-14,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178411,-49.387605\",-20.8178411,-49.387605,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/mpsz_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95862Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: RedentoraM2 rea Construida: 190, Garagem Descoberta: 02, Forro de Laje, Piso Salas: Frio\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Redentora,https://thumbs4.properati.com/7/xW33i6TXrwRgMFrpyW_syKKKlPs=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1281/UKC5P4P7lZPyQSY9l8xC.jpg\n2128d47314a494ddecda6b6e981c46afbd3fae51,2014-10-30,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/s9we_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95921Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Redentora..: Salo Comercial Na Redentora, Salo Comercial, M2/ Terreno: 150, Detalhes: tima Localizao!\",Ref.: SA95921 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Redentora,https://thumbs4.properati.com/6/HVrSVpwKgIJpnnFxGcMzyHIeUx4=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2347/abTB8PxHKedvADO9yyhF.jpg\n0329fa7a5536c99eee743612010897cafed7f340,2014-10-15,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8030634,-49.4324803\",-20.8030634,-49.4324803,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/re15_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95910Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Res. Palestra..: Salo Comercial No Residencial Palestra, Salo Comercial, Wc. Social: 02, Forro de Laje, Obs: timo Salo, Novo., M2/ Terreno: 250, M2 rea Construida: 200\",Ref.: SA95910 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Res. Palestra,https://thumbs4.properati.com/8/MBuRdEvx5x-V-zpeycnxFNg-n70=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2238/NOqLX7BtA7KEH0SUs5rV.jpg\nc822a8819d8874916877cc6b3c91194cbf5968ca,2014-10-09,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7780086,-49.3450562\",-20.7780086,-49.3450562,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/r9j6_alugar_casas_sao-jose-do-rio-preto_avenida-belvedere,\"Ref.: CA95903Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Village Damha Rio Preto I..: Casa Condomnio Village Damha Rio Preto I, Casa Condominio, Dormitrios: 03, Tipo Apto.: 03, C/ A/e: 03, Cozinha: 01, Cozinha Planejada, Fogo, Lavabo, Salas : 01, Sala P/ Ambientes : 02, Piscina, Ducha Externa, Varanda, rea de Servio, Wc. de Empregada/servio, Garagem Coberta: 02, Churrasqueira\",Ref.: CA95903 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Village Damha Rio Preto I,https://thumbs4.properati.com/2/595nD1yJ6OYSKSxIr0D1iMhNilQ=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2205/060de194-4e66-4f1e-b0cc-dd05e276539f.jpg\n2568b8aa05b9d3cb3e9e8ef74c4461c34d2d57b5,2014-10-05,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8350772,-49.369111\",-20.8350772,-49.369111,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/r1q8_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95905Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Quinta das Paineiras..: Salo Comercial No Quinta das Paineiras, Salo Comercial, Terrea, Cozinha: 01, Escritrio, M2 rea Construida: 225\",Ref.: SA95905 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Quinta das Paineiras,https://thumbs4.properati.com/6/2tu7W_u475v3n8Ubll7avmjLXPI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2130/0y7ijJHaZqFbrC0LSLFy.jpg\n0dff14f277324eb9f04d59b6c26758ccdb744e27,2014-12-05,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8126181,-49.3404534\",-20.8126181,-49.3404534,3700,BRL,2925.47,947.43,,,,,,,,http://www.properati.com.br/udzy_alugar_casas_sao-jose-do-rio-preto_avenida-miguel-damha,\"Ref.: CA95938Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha III..: Linda Casa Tipo Sobrado No Condomnio Damha Iii, Casa Condominio, Sobrado, Dormitrios: 03, Suite Master: 01, C/ Hidro, Tipo Apto.: 02, C/ A/e, Ducha Dupla, Lavatrio Duplo, Cozinha: 01, Cozinha Planejada, Despensa, Escritrio, Lavabo, Salas : 01, Sala P/ Ambientes : 02, Varanda, rea de Servio, Lavanderia, Quarto de Empregada, Wc. de Empregada/servio, Forro de Laje, Garagem Descoberta: 02, Garagem Coberta: 02, Churrasqueira\",Ref.: CA95938 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha III,https://thumbs4.properati.com/0/FGZdTzG9TG6qqioIMRxZ7WLDQWY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2452/bVU2c6naoVF7KCpphJqL.jpg\n6b3bff7cc70a79dfed41fc0ae514a7ad16a328b8,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,7000,BRL,6640.22,2150.47,,88,24.437159090909088,79.54545454545455,,,,http://www.properati.com.br/ifvc_alugar_comercial_vila-olimpia,\"\tEdifício comercial de excelente padrão, localizado em ponto nobre da Vila Olímpia, próximo da Av. dos Bandeirantes e Shopping Vila Olímpia.\t Prédio conta com 3 elevadores de alta velocidade, sistema de proteção contra incêndio, ar condicionado central, próximo a bancos. Referíªncia: CO65\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",\nc26e111d703af6f65be91df94c274bbd41585a40,2014-08-21,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.6357809,-46.5324419\",-23.6357809,-46.5324419,50000,BRL,39533.44,12803.11,,,,,,,,http://www.properati.com.br/nqbl_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95307Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Rodovia Assis ChateaubriandWcs.: 4, Escritrio, M2/ Terreno: 4.000, Detalhes: Barraco Com 4.000 M\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Rodovia Assis Chateaubriand,https://thumbs4.properati.com/0/lJ2ffPr7gtS2vlQuGTg_zDX6844=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1592/WQmB3JTS7Y2N72aUI5Ud.jpg\n2e8ffab1b55b5245e917b3597009338cb92b6586,2014-10-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7807632,-49.3985157\",-20.7807632,-49.3985157,50000,BRL,39533.44,12803.11,,,,,,,,http://www.properati.com.br/rgyw_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95804Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Waldemar VerdiM2/ Terreno: 6.000, M2 rea Construida: 3.200\",Salo,https://thumbs4.properati.com/3/X38EXdJIkvCXPQCdWURfrgMMMWQ=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2270/OTptGk75zRPMzZ1pL1on.jpg\nc522d5f57ea8e14d18fa3ba746cb581cd379330a,2014-08-20,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,100000,BRL,79066.88,25606.22,,,,,,,,http://www.properati.com.br/np98_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95721Tipo: Salo IndustrialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial IWcs.: Vrios, M2/ Terreno: 13.000, M2 rea Construida: 8.500, Detalhes: tima Localizao\",Tipo: Salo Industrial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial I,https://thumbs4.properati.com/6/jARKWDWV0rGe-HzGUtUeE0X3knw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1549/ZaCxToSHCaeCL7GxZ08g.jpg\n07383fafc72a9aa9f0286b841b782ca718485de8,2014-09-01,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178198,-49.3468294\",-20.8178198,-49.3468294,3500,BRL,2767.31,896.21,,,,,,1,,http://www.properati.com.br/o246_alugar_casas_sao-jose-do-rio-preto_avenida-nadima-damha,\"Ref.: CA95827Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha II (Res. Jardins)Dormitrios: 03, Suite: 03, C/ A/e: 03, Sacada, Sala de Visita, Copa, Cozinha, Cozinha Planejada, Despensa, Piscina, rea de Servio, Lavanderia, Wc. de Empregada/servio, Garagem Descoberta: 02, Garagem Coberta: 02, Churrasqueira, Acabamento Em Gesso, Sanca, Forro de Laje, Piso Salas: Frio;, Documentao: Em Dia.\",Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha II (Res. Jardins),https://thumbs4.properati.com/6/Bq9TbjreWNnjd2VrvJ5oieGegeQ=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1889/db5caa0b-ec86-44f4-9c0f-ab26a6816b62.jpg\n179f70a47a9ce50e161ea483ffee741a8e1c7fb2,2014-08-14,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178411,-49.387605\",-20.8178411,-49.387605,3500,BRL,2767.31,896.21,,,,,,,,http://www.properati.com.br/mpsx_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95861Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Redentora TerreaM2 rea Construida: 190, Acabamento Em Gesso, Forro de Laje, Pintura Nova, Piso Salas: Frio\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Redentora Terrea,https://thumbs4.properati.com/1/VQPzYdCL1X2L1IkzU17WHDuej9s=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1279/nGCLTL2QhwMJSHGtxR7y.jpg\ne48c41f6ad3dba93e0ac6038a5aa5cda39d651d0,2014-09-01,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,3500,BRL,2767.31,896.21,,,,,,,,http://www.properati.com.br/o2m6_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95898Tipo: Casa Comercial, Casa ResidencialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel Haddad..: Casa Residencial No Moyss Miguel Haddad, Casa Residencial, Dormitrios: 03, Suite: 03, C/ Hidro: 01, C/ A/e: 03, Sacada, Wc. Social: 01, Wcs. C/ A/e, Sala de Visita: 01, Copa: 01, Cozinha: 01, Cozinha Planejada, Despensa, Escritrio, Salas : 01, Sala P/ Ambientes : 03, Piscina, Varanda, rea de Servio, Lavanderia, Quarto de Empregada, Wc. de Empregada/servio, Ar Split, Ventilador, Interfone, Acabamento Em Gesso, Sanca, Tv Cabo, Campainha, Quintal, Grade, Murado, Forro de Laje, Piso Salas: Frio, Obs: Imvel Prximo Ao Shopping Iguatemi, M2 rea Construida: 450, Garagem Descoberta: 04, Garagem Coberta: 02, Churrasqueira, Jardim de Inverno, Jardim Interno, Iptu Valor: 199,00\",\"Ref.: CA95898 Tipo: Casa Comercial, Casa Residencial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad\",https://thumbs4.properati.com/5/fZwdpPACH02ObP9aj7-UFvVm0lE=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1937/a7WmbhKqWo7JqFgAHcOD.jpg\n9ea36b0e269fbf2581eef1510f0148594919b330,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,3150,BRL,2988.09,967.71,,45,21.50466666666667,70,,,,http://www.properati.com.br/ifxf_alugar_comercial_chacara-santo-antonio,\"\tNovAmérica Inside Park, o novo marco imobiliário de São Paulo. É um novo espaço, um novo bairro, uma nova opção de vida. É a expansão urbana mais consciente e mais urbana. Um mundo para morar, trabalhar e viver em plena harmonia. Uma região onde a vida social e a vida familiar se equilibram. Um lugar que concretiza o prazer de viver em uma grande metrópole. 1 torre comercial, escritórios de vila e lojas, 5 condomínios residenciais independentes e 2 parques em mais de 70.000 m² de terreno.\t\tNovAmérica Office Park é um conceito totalmente novo de local de trabalho, que valoriza eficiência e a qualidade de vida de seus ocupantes. EDSA em parceria com Neusa Nakata tiveram a missão de conceber um produto inusitado, diferenciado e particular. O resultado é um cenário privilegiado para que você e seus clientes tenham bem-estar em horário comercial.\t\tA região do NovAmérica reúne as principais empresas nacionais e multinacionais, que chegaram por aqui atraídas pela infraestrutura e pelas importantes e fáceis vias de acesso. O que confirma que a região é o novo pólo econômico de São Paulo.\t\tConheça os diferenciais do NovAmérica Office Park:\t\tÚnico na Nações Unidas a integrar um complexo formado por 1 torre comercial, escritórios de vila, lojas e 5 condomínios residenciais independentes.\tConceito consagrado de bem-estar em horário comercial.\tAcesso ao parque com mais de 10 mil m² destinado à cidade.\tPotencial de clientes vindos dos 5 condomínios residenciais.\tA oportunidade de morar e trabalhar de forma integrada e independente.\tServiços by Facilities Office.\tArquitetura e decoração inspirados nas ultimas tendências mundiais.\tProjeto paisagístico assinado por um dos mais conceituados escritórios do mundo, a EDSA. Referíªncia: CO150\",\"Comercio no(a) Chácara Santo Antônio , São Paulo - SP\",https://thumbs4.properati.com/2/2S3OsO4umzIVXFHbHKTrb7dk-x8=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/150/c0d9b8fc-3da8-4a27-ac1f-4e1684d35312.jpg\na5c14fe842664d12fe4bdfad313d5af251121d89,2014-10-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5882535,-46.6167776\",-23.5882535,-46.6167776,1400,BRL,1106.91,358.48,,,,,,,,http://www.properati.com.br/qz98_alugar_comercial_sao-jose-do-rio-preto_rua-da-imprensa,Ref.: SA95836Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Vila DinizDetalhes: Salo Comercial Com Aproximadamente 50 M e 02 Banheiros Com Acessibilidade.,Ref.: SA95836 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Vila Diniz,https://thumbs4.properati.com/9/CRgjdJjcRp1Lz-pPZChI7eL8ZIY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2075/fOuQlJsX61PBHGGwhPSh.jpg\nb64a9bd212d9e91821e5e892eeff464bbc0202cd,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,9600,BRL,9106.57,2949.21,,120,24.57675,80,,,,http://www.properati.com.br/ifwi_alugar_comercial_vila-olimpia,\"\tConjunto Comercial com 120m² de área útil, 3 vagas, 3 banheiros, 1 copa. Referíªncia: CO112\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/0/3pwYR5bzXi_fuQfUVbzkPbFkst4=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/112/93f2b4dc-86f7-4544-803f-62dfec9dc967.jpg\n59a2898d29cb6860a70355c1b2028b9ab38d89d9,2015-01-13,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5456177,-46.563645\",-23.5456177,-46.563645,1801,BRL,1459.35,472.62,,,,,,2,,http://www.properati.com.br/vyso_alugar_apartamentos_tatuape,\"APTO DE 75M/2, COM 03 DORMITRIOS, SALA AMPLA COM SACADA, COZINHA PLANEJADA, WC SOCIAL, REA DE SERVIO COM WC, REPLETOS DE ARMRIOS, 01 VAGA DE GARAGEM FIXA, EXCELENTE ESTRUTURA DE LAZER COM PISCINA, PLAYGROUD, SALO DE FESTA, CHURRASQUEIRA, SALA DE GINASTICA, EXCELENTE LOCALIZAO, A 100 METROS DO METR.\",LOCAO TATUAPE CONDOMINIO ILHAS GREGAS,https://thumbs4.properati.com/6/-xzzzVWk3lxFITvW285d3YmP00c=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/13/e7c88a20-dcac-49b2-ae42-e782447d3f07.jpg\n1925426e5575ecd98f1e63422a187081754e0f5c,2015-01-13,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5444872,-46.5657875\",-23.5444872,-46.5657875,1499,BRL,1214.64,393.37,,,,,2,1,,http://www.properati.com.br/vysp_alugar_apartamentos_tatuape_rua-serra-de-braganca,\"APTO DE 40M/2, COM 01 DORMITRIO, SALA AMPLA COM SACADA, COZINHA, WC SOCIAL, SEMI-MOBILIADO COM FOGO, SUGGAR, GELADEIRA, MICRO-ONDAS, ARMRIOS PLANEJADOS NA COZINHA, CAMA BOX, GUARDA ROUPA, VENTILADOR DE TETO, RACK DE TV, 02 BANQUETAS, 02 MESINHAS DE CANTO, EXCELENTE ESTRUTURA DE LAZER COM PISCINA, PLAYGROUD, SALO DE FESTA, CHURRASQUEIRA, SAUNA, SALA DE FITINESS, BRINQUEDOTECA, SALO DE JOGOS, SALA DE LEITURA. EXCELENTE LOCALIZAO, PRXIMO DO SUPERMERCADO PO DE ACAR, FCIL ACESSO PARA O METR.\",NEW CONCEPT 22 A,https://thumbs4.properati.com/5/SXF7T5Zfxal6lcQifCYOufBKjYA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/14/0522b83e-c37e-4736-ac81-ca8540d2d806.jpg\nfda45d95b6fe8daf9215dcc80ad057531879eda4,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,4575,BRL,4339.84,1405.48,,120,11.712333333333333,38.125,,,,http://www.properati.com.br/ifxz_alugar_comercial_vila-olimpia,\"\tConjunto Comercial na Vila Olimpia, com 61m², podendo aumentar a área para 117m². Referíªncia: CO179\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/5/cZY_8L_8bfKOV9ay-F-6DsnRZ-c=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/179/0df8ffa9-49a9-49c0-9968-37c13719d904.jpg\n7eb9fb8ad21ff1da490b75fd04643939fd72edf1,2015-01-13,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5508578,-46.5679013\",-23.5508578,-46.5679013,1550,BRL,1255.99,406.76,,,,,11,1,,http://www.properati.com.br/vysn_alugar_apartamentos_tatuape,\"APTO DE 57M/2, COM 02 DORMITRIOS, SALA AMPLA COM SACADA, WC SOCIAL, COZINHA PLANEJADA, AMPLA REA DE SERVIO COM WC, 01 VAGA DE GARAGEM, SALO DE FESTA, CHURRASQUEIRA, PLAYGROUD, QUADRA DE FUTEBOL, EXCELENTE LOCALIZAO, PRXIMO AO SHOPP/METR BOULEVAR TATUAP.\",Apartamento,https://thumbs4.properati.com/4/mwisK4iz0H0viKmBVOGr8B40c1o=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/12/612ab301-0ac4-4c4c-9691-7278200c2b62.jpg\n59a2934bc5a4c3530613fae471a210ae63498d80,2014-09-02,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.826094,-49.376396\",-20.826094,-49.376396,1200,BRL,960.76,311.15,,,,,,1,,http://www.properati.com.br/ob16_alugar_apartamentos_sao-jose-do-rio-preto,\"Apartamento mobiliado, pronto para morar e com excelente localizao, na zona sul.2 dormitrios sendo 1 tipo apto todos com armrio, ventilador de teto e ar-condicionadosala com sacada para 2 ambientes com sofa e estantecozinha planejada com mesa com 4 cadeiras e fogo2 banheiros com box blindex, gabinete e chuveirorea de servio com armrio, varal e mquina de lavar1 vaga de garagem pilotisCondomnio: Porto eletrnico, portaria 24h, elevador, churrasqueira e piscina.\",Apartamento com 2 dormitrios em Rio Preto,https://thumbs4.properati.com/5/VXcYDJRN6VtNxvgVw-mxJOlL_sA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/premier/photos/111/91c65753-86b6-40fd-8673-7f8f107a8459.jpg\nd08581768058f5286ca4f209222d59a3f10af84d,2015-01-13,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.8877586,-47.081265\",-22.8877586,-47.081265,2401,BRL,1945.56,630.08,,,,,19,2,,http://www.properati.com.br/vysm_alugar_apartamentos_tatuape,\"APTO DE 93M/2, COM 03 DORMITRIOS, SENDO UMA SUTE, SALA AMPLA COM SACADA E DOIS AMBIENTES, WC SOCIAL, COZINHA, AMPLA REA DE SERVIO, REPLETO DE ARMRIOS, 02 VAGAS DE GARAGEM, LAZER COMPLETO COM PISCINA, PLAYGROUD, SALO DE FESTA, SALO DE JOGOS, SAUNA, CHURRASQUEIRA, EXCELENTE LOCALIZAO, PRXIMO DA PRAA SILVIO ROMERO.\",APTO LOCAO TATUAP,https://thumbs4.properati.com/3/d45ivnQIryA-xDQvwZrOc1pBPlE=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/11/85e5aee8-90fa-4295-bcb5-dce1ed7e789c.jpg\n7739326a3915df1654866f5c27d42e40f0038365,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2200,BRL,2086.92,675.86,,102,6.626078431372549,21.568627450980394,,,,http://www.properati.com.br/ifwp_alugar_comercial_vila-olimpia,\"\tConjunto Comercial com 46m², 2 banheiros, 3 salas, 1 vaga. Referíªncia: CO122\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/6/QhZrO4ITAg_PJagrwPKR0ywe4GI=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/122/80e20053-aaba-4f3b-a8f7-9dc2cdcb44a7.jpg\n4d949c2fcb78e76cf0cb2376ae3a3acc3b27994a,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2200,BRL,2086.92,675.86,,46,14.692608695652174,47.82608695652174,,,,http://www.properati.com.br/ifut_alugar_comercial_vila-olimpia,\"Conjunto Comercial na Rua Helena, com 46m², 1 vaga. Referíªncia: CO44\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/7/rah-1WLiSRF_nrLl-U2mEY27Gus=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/44/3d3a55f5-dd0c-4969-bb57-19f9a554c1f2.jpg\nc98dc531eb6cb68bcec0e60d7dc042bd663caa4d,2014-08-02,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.815191,-49.389435\",-20.815191,-49.389435,5000,BRL,4003.24,1296.47,,,,,,,,http://www.properati.com.br/ludd_alugar_casas_sao-jose-do-rio-preto,\"Imvel com excelente localizao, com recepo, 1 sala com banheiro privativo, 3 salas amplas + 1 interna, todos os ambientes com ar condicionado, cozinha, despensa, garagem interna para vrios veculos. IMVEL SER ENTREGUE REFORMADO E COM AS ACESSIBILIDADES EXIGIDAS!\",Casa Comercial,https://thumbs4.properati.com/8/3TeTIOIgyjtraKZz08buZLIU8W8=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/premier/photos/80/6a0fcb3e-3e78-434f-9624-a5621798b2b2.jpg\ne85c67a2bc55f4834be8d4cf1fd96340ef4ce129,2014-08-04,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8379877,-49.385659\",-20.8379877,-49.385659,800,BRL,632.5,204.84,,,,,,1,,http://www.properati.com.br/lxpc_alugar_casas_higienopolis,\"Ref.: AP95874 Tipo: ApartamentoCidade: So Jos do Rio Preto - SPBairro: Higienpolis..: Apartamento Novo No Jardim Higienopolis, Apartamento, rea Util M2: 50, Dormitrios: 01, C/ A/e, Sacada, Wc. Social: 01, Cozinha C/ A/e, Lavanderia, Elevador, Piso Dormitrios: Porcelanato, Piso Salas: Porcelanato, Obs: Prdio Novo.apto Nunca Habitado., rea de Lazer, Garagem Coberta: 01, Churrasqueira, Espao Gourmet\",Tipo: Apartamento Cidade: So Jos do Rio Preto - SP Bairro: Higienpolis,https://thumbs4.properati.com/6/8tmxgf5PTdg11vI4rVdPwXy39gw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/806/2077f853-c360-4cd7-b5c8-a33f76415c40.jpg\n33b22b30a043e5a868df61862bb97698b453df89,2014-12-05,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7910821,-49.3781115\",-20.7910821,-49.3781115,800,BRL,632.5,204.84,,,,,,1,,http://www.properati.com.br/ue05_alugar_apartamentos_sao-jose-do-rio-preto_rua-pedro-mulati,\"Ref.: AP95816Tipo: ApartamentoCidade: So Jos do Rio Preto - SPBairro: Jardim ConceioDormitrios: 02, C/ A/e, Wc. Social, Box Wcs., Sala de Estar, Cozinha, Piscina, rea de Servio, Garagem Coberta: 01, rea Util M2: 60, Condominio C/ rea de Lazer, Piscina Adulto, Playground, Valor Condominio R$-: 190,00\",Ref.: AP95816 Tipo: Apartamento Cidade: So Jos do Rio Preto - SP Bairro: Jardim Conceio,https://thumbs4.properati.com/2/4RA-EyT8LswOeQ6lcBbkzLlKjII=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2459/jWyxMNGlETMMxgj2uW9o.jpg\ne2235d97414fcc1a31865b1abe71c1cf85ae01fa,2014-11-17,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7780086,-49.3450562\",-20.7780086,-49.3450562,2700,BRL,2134.78,691.36,,,,,,,,http://www.properati.com.br/tdi5_alugar_casas_sao-jose-do-rio-preto_avenida-belvedere,\"Ref.: CA95926Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Village Damha Rio Preto I..: Casa Condomnio Village Damha Rio Preto, Casa Condominio, Terrea, Dormitrios: 3, Suite: 1, Tipo Apto.: 2, C/ A/e, Lavatrio Duplo: 1, Cozinha C/ A/e: 1, Cozinha Planejada, Escritrio, Lavabo, Salas : 1, Sala P/ Ambientes : 2, Varanda, rea de Servio, Wc. de Empregada/servio, Obs: Espao Para Colocar Piscina, M2 rea Construida: 180, Garagem Descoberta: 2, Garagem Coberta: 2, Churrasqueira, Detalhes: Com Armrios .\",Ref.: CA95926 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Village Damha Rio Preto I ,https://thumbs4.properati.com/3/ZZ3MVCiFvwDaZxqgfl5oQJ1Vp6c=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2390/5d2ae653-6549-4772-b486-cd9360af6ebd.jpg\n1334a26f10895f5a21b6db0adf96e2ff1dc83799,2014-09-16,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7780086,-49.3450562\",-20.7780086,-49.3450562,1250,BRL,988.31,320.07,,,,,,,,http://www.properati.com.br/q4jz_alugar_casas_sao-jose-do-rio-preto_avenida-belvedere,\"Ref.: CA95901Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Maria Stella..: Casa Condomnio Maria Stella, Casa Condominio, Terrea, Dormitrios: 3, Tipo Apto.: 1, Wc. Social: 1, Cozinha: 1, Cozinha Planejada, Salas : 01, Sala P/ Ambientes : 02, Varanda, rea de Servio, Lavanderia, Garagem Coberta: 02, Salo de Festa, Playground, Piscina Adulto, Quadras Poliesportivas: 1, Campo de Futebol: 1, Valor Condominio R$-: 260,00\",Ref.: CA95901 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Maria Stella,https://thumbs4.properati.com/3/nKZAghWN-90yQywuniXpPPLO9sk=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2011/e55aad60-d657-4c71-b732-96032067b76f.jpg\n40c34e74b932ff6e10ff49774a8f7fb6e982c1c2,2014-11-30,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8081017,-49.3505771\",-20.8081017,-49.3505771,4500,BRL,3558.01,1152.28,,,,,,,,http://www.properati.com.br/tz24_alugar_comercial_sao-jose-do-rio-preto_rua-valdomiro-nadotti,\"Ref.: SA95928Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Tres Maria..: Salo Comercial No Bairro Trs Maria, Salo Comercial, Detalhes: 160 Metros Mais 40 Metros de Mesanino.\",Ref.: SA95928 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Tres Maria,https://thumbs4.properati.com/3/Yg9eVpfZKwd2s12w6w1yQ4YYVhY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2402/MTVaVFERWIxvf0IcXQaQ.jpg\n716428a7c9f46f98fd78e36a72109b0c0ec43753,2014-11-30,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8081017,-49.3505771\",-20.8081017,-49.3505771,4500,BRL,3558.01,1152.28,,,,,,,,http://www.properati.com.br/tz25_alugar_comercial_sao-jose-do-rio-preto_rua-valdomiro-nadotti,\"Ref.: SA95929Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Tres Maria..: Salo Comercial No Bairro Trs Maria, Salo Comercial, M2/ Terreno: 200, Detalhes: 140 Metros Mais 40 Metros de Mesanino.\",Ref.: SA95929 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Tres Maria,https://thumbs4.properati.com/9/a_rVYKLimoBl774T612e8o5Icfo=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2403/fhkihJbLNgNCmHo7eBRG.jpg\n2102860e4c561ee2afec1c15fb71f7c6cfafc28d,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,9000,BRL,7116.02,2304.56,,,,,,,,http://www.properati.com.br/muuy_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95893Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Ch. Jockey Clube..: Salo Comercial Na Chcara Jockey Clube, Salo Comercial, M2/ Terreno: 5.000, M2 rea Construida: 1.200, Detalhes: Galpo Com Piso Cimento Polido, Estrutura Metlica, Escritrio, Cozinha, 02 Wc, Entrada Pela Frente e Lateral Para Caminho.p Direito Alto Com 6,/2 M.\",Ref.: SA95893 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Ch. Jockey Clube,https://thumbs4.properati.com/3/H-b0Ol05_NX0A5soGXBvD_dA3wk=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1482/gCB4KEDxOoIlG9HlGbCW.jpg\ndb16c4572d4f19875153b041d1dcb6b82115e6a7,2014-11-03,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8174527,-49.4306981\",-20.8174527,-49.4306981,18000,BRL,14232.04,4609.12,,,,,,,,http://www.properati.com.br/sjyy_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95923Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Parque Industrial..: Salo Comercial No Parque Industrial, Salo Comercial, M2/ Terreno: 3.000, M2 rea Construida: 1.780, Detalhes: 1.000m de Patio.escritrio Com Elevado Vrias Salas, Cozinha, Wc, Vestirio Mas/fem;04 Salas de Descanso Para Motorista Com Vestirio, 02 Chuveiros e 04 Wc;cozinha Externa Com Quiosque + 02 Wc Mas/fem;parte Operacional Com P Direito de 7 Metros, e A Lateral Toda Com Embarque e Desembarque;ptio Com Espao Para Carretas, Caminhes e Carros;portaria Na Entrada do Ptio.\",Ref.: SA95923 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Parque Industrial,https://thumbs4.properati.com/0/wtHJ8Xcu4pqyV7YSP06g_N1yrfg=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2356/rO0uXvqSM9ilLshNDz0f.jpg\nc8c81389391a237255bade4a0608212fa62e13b1,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,4200,BRL,3984.12,1290.28,,107,12.058691588785047,39.25233644859813,,,,http://www.properati.com.br/ifxy_alugar_comercial_vila-olimpia,\"\tConjunto Comercial na Vila Olimpia, com 56m² de área útil, excelente localização.  Referíªncia: CO178\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/7/YjqSlFDSnOcayBrp2UtD_X2xaJM=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/178/fa4df6b6-bb36-40a4-a46b-7b1438c77d03.jpg\ne1f129a3f1296a7e7a3b60d5b96d61c49159900c,2014-11-19,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.9295411,-46.3418105\",-23.9295411,-46.3418105,1600,BRL,1628.47,527.39,,32,16.4809375,50,,1,,http://www.properati.com.br/tkvf_alugar_apartamentos_sao-paulo,\"A 3 Milenio oferece este(a) Apartamento em Sao Vicente, no bairro Morro dos Barbosas - 1 dormitórios. Gostou desse(a) Apartamento em Sao Vicente? Então veja mais informações! Acesse o nosso site clicando no botão Contate o anunciante ou ligue - ou ainda entre em contato conosco pelo email vendas@ Obrigado!\",\"Apartamento em Sao Vicente, no bairro Morro dos Barbosas - 1 dormitórios\",https://thumbs4.properati.com/5/FvlCdG4KWDtvmzsszDUttj3AuAY=/trim/198x0/smart/filters:strip_icc()/www.imobiliariasdesantos.com.br/fotos/F00/72/807200-DSC03573.JPG\na8821d1c24cccd45d37ef69cf2b29ef3be744fad,2014-11-17,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8456698,-49.4030718\",-20.8456698,-49.4030718,3200,BRL,2530.11,819.39,,,,,,1,,http://www.properati.com.br/tdi2_alugar_apartamentos_sao-jose-do-rio-preto_avenida-anisio-haddad,\"Ref.: AP95925Tipo: ApartamentoCidade: So Jos do Rio Preto - SPBairro: Cond. Green Fields..: Apartamento No Condomnio Green Fields, Apartamento, rea Util M2: 152, Dormitrios: 03, Suite Master: 01, Closet , Tipo Apto.: 02, C/ A/e: 02, Wc. Social: 02, Sala de Visita: 01, Cozinha Planejada, Garagem Coberta: 02\",Ref.: AP95925 Tipo: Apartamento Cidade: So Jos do Rio Preto - SP Bairro: Cond. Green Fields,https://thumbs4.properati.com/4/oRIx16Fpw4LPkRsB59wlBS_mxVo=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2387/ae2a6d87-5760-44ff-a47f-d237a5321c3c.jpg\n08918f4a93c54f4505b783a8e31ef11842c55e56,2014-10-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5882535,-46.6167776\",-23.5882535,-46.6167776,80000,BRL,63253.52,20484.98,,,,,,,,http://www.properati.com.br/qz9a_alugar_comercial_sao-jose-do-rio-preto_rua-da-imprensa,\"Ref.: SA95835Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Vila DinizM2/ Terreno: 12.000, Detalhes: - Salo Com 5.000 M e Bastante rea Para Estacionamento Aberto.Ideal Para Atacadista, Hipermercado e Distribuidora, Entre Outros.\",Ref.: SA95835 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Vila Diniz,https://thumbs4.properati.com/1/PkKXX6cNiwu9krqBtdWDinLG89A=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2077/vSsX8s9IzXdbNCXPId0W.jpg\n1ce358d0b5c35f146d70dff4089a8b3d287cb7b9,2014-10-08,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8085627,-49.3452813\",-20.8085627,-49.3452813,5500,BRL,4348.67,1408.34,,,,,,,,http://www.properati.com.br/r6de_alugar_comercial_sao-jose-do-rio-preto_avenida-sebastiao-tavares-da-silva,\"Ref.: SA95809Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Vista AlegreM2/ Terreno: 360, M2 rea Construida: 360, Detalhes: Salo Com Vrias Divisrias.\",Salo,https://thumbs4.properati.com/8/dhtnMWfIFZ8mMCFVDpxpvjoF0LA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2184/l63oynNjEecn8u1Dzf3W.jpg\nb6ed51b61bb57831cc00d904741b4f544cafc28f,2014-08-04,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178198,-49.3468294\",-20.8178198,-49.3468294,5500,BRL,4348.67,1408.34,,,,,,1,,http://www.properati.com.br/lxoz_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95886Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha II(Res. Jardins)..: Casa Condomnio Damha II, Casa Condominio, Sobrado, Dormitrios: 03, Suite: 01, C/ Hidro, Tipo Apto.: 02, C/ A/e: 03, Roupeiro, Home, Cozinha: 01, Cozinha Planejada, Despensa, Escritrio, Lavabo, Salas : 01, Sala P/ Ambientes : 03, Piscina, Varanda, rea de Servio, Quarto de Empregada, Wc. de Empregada/servio, Aquecedor Solar, M2 rea Construida: 350, rea de Lazer, Garagem Descoberta: 03, Garagem Coberta: 03, Churrasqueira\", Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha II,https://thumbs4.properati.com/1/gEw1FIkmFLn2_zGBjqnuUzcifYU=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/793/e9d2d588-8d92-4070-b7d6-5b55e4484038.jpg\n1273c64fb8b9402dfad98d3984dec2998d8bdb68,2014-08-15,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8126181,-49.3404534\",-20.8126181,-49.3404534,5500,BRL,4348.67,1408.34,,,,,,1,,http://www.properati.com.br/mqtl_alugar_casas_sao-jose-do-rio-preto_avenida-miguel-damha,\"Ref.: CA95731Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha IVDormitrios: 03, Suite: 01, Closet : 01, C/ Hidro, Tipo Apto.: 02, C/ A/e: 02, Wcs.: 05, Wcs. C/ A/e: 04, Box Wcs.: 04, Copa: 01, Cozinha: 01, Cozinha C/ A/e, Despensa, Escritrio, Lavabo, Salas : 01, Sala P/ Ambientes : 02, Piscina, Varanda, rea de Servio, Wc. de Empregada/servio, M2/ Terreno: 420, M2 rea Construida: 380, rea de Lazer, Garagem Descoberta: 02, Garagem Coberta: 02, Churrasqueira, Piso Dormitrios: Porcelanatoq, Piso Salas: Porcelanato, Obs: Imvel Muito Bem Conservado, timo Acabamento., Valor Condominio R$-: 300,00, Detalhes: Toda Reformada.\",Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha IV,https://thumbs4.properati.com/6/H5vuIYMzk8pMuWJ-OjcdH4GItq8=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1367/9717e95d-fe6f-4cbc-bb5d-21c2fe92d294.jpg\n6aa6bf8a204f123441efb9f97699ee24cf8f9932,2014-08-15,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178198,-49.3468294\",-20.8178198,-49.3468294,5500,BRL,4348.67,1408.34,,,,,,1,,http://www.properati.com.br/mqtn_alugar_casas_sao-jose-do-rio-preto_avenida-nadima-damha,\"Ref.: CA95797Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha ISobrado, Dormitrios: 03, Suite: 01, Tipo Apto.: 02, Wcs.: 01, Cozinha Planejada, Escritrio, Lavabo, Salas : 01, Sala P/ Ambientes : 03, Piscina, rea de Servio, Wc. de Empregada/servio, rea de Lazer, Garagem Coberta: 03, Churrasqueira, Ar Condicionado, Ventilador, Interfone, Piso Dormitrios: Taco, Piso Salas: Frio\",Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha I,https://thumbs4.properati.com/0/NAjAu7cPHQwzwT2j3k1G4X7-A3U=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1369/7b6ca8dd-648a-4e18-b9f5-272073bfbf69.jpg\n8a977c0c4b9ef700024cb2ee3e59de53bcf599c8,2014-07-16,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2000,BRL,1897.2,614.42,,70,8.77742857142857,28.571428571428573,,2,,http://www.properati.com.br/ifzl_alugar_apartamentos_vila-olimpia,\"\tApartamento na Vila Olimpia, sala para 2 ambientes, 2 dormitórios com armários embutidos, cozinha, lavanderia, quarto na área de serviço, 2 banheiros, 1 vaga. Referíªncia: AP237\",\"Apartamento no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/9/waPGo7zWMk2XTzN-br6B0h4Nrgo=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/237/75daca8f-ee37-4813-97af-b4fa00920d4f.jpg\nb48b18eca01fc2732d6afe024904eb1e900cc52a,2014-07-16,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2000,BRL,1897.2,614.42,,54,11.378148148148147,37.03703703703704,,2,,http://www.properati.com.br/ifzn_alugar_apartamentos_saude,\"\tÓtimo apartamento proximo ao Metrê Praça da Arvore, sala para 2 ambientes, 2 dormitórios com armários embutidos, cozinha com armários, banheiro social e de serviço, 1 vaga na garagem, quadra, piscina e play.\tCom pintura nova, pronto para morar!! Referíªncia: AP239\",\"Apartamento no(a) Saúde , São Paulo - SP\",https://thumbs4.properati.com/0/Zof1-heqqvAgC3xdyYRYiQHIX2I=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/239/450071da-5288-4f26-bbb4-66bcacaef572.jpg\n636cbd951ef34773973cfdef37746b911d07a24c,2014-07-16,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,4000,BRL,3794.41,1228.84,,200,6.1442,20,,4,,http://www.properati.com.br/ifxa_alugar_casas_saude,\"\tAluga sobrado em zona mista, residencial e ou serviços, a uma quadra da Av. do Cursino e a 1000m, do Shopping Plaza Sul, 4 dormitórios, sendo uma suite, ampla sala, mais sala de jantar, cozinha, edicula com 3 quartos, mais lavanderia, garagem para 3 veiculos, casa de esquina, com portões automaticos.  Referíªncia: CA145\",\"Casa no(a) Saúde , São Paulo - SP\",https://thumbs4.properati.com/3/0sie3N08lMquoZ655zNy1CeDUbU=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/145/9fef5234-2fa4-4974-b24c-2b3cf6c4c542.jpg\n0812a419c00dcbaaaa926ad4e8de47da7959f9d9,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,5700,BRL,5407.01,1751.09,,127,13.788110236220472,44.881889763779526,,,,http://www.properati.com.br/ifzp_alugar_comercial_saude,\"\tConjunto Comercial proximo do Metrô São Judas, com 109m² de área útil, dividido em 5 salas, com possibilidade de vão livre, 3 banheiro, 1 copa, 3 vagas, prédio 24 horas.  Referíªncia: CO241\",\"Comercio no(a) Saúde , São Paulo - SP\",https://thumbs4.properati.com/1/0kl3BRCM4riJX3LhCObIu4wuBxQ=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/241/25ac4ee5-f867-4808-b226-837aefd7ba2c.jpg\n570fece17687d05345022d1567c79dd80bd1dcb0,2014-12-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8312527,-49.360102\",-20.8312527,-49.360102,15000,BRL,11860.02,3840.93,,,,,,,,http://www.properati.com.br/viin_alugar_comercial_sao-jose-do-rio-preto_avenida-marginal-lineu-alcantara-gil,\"Ref.: SA95950Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Campo Verde..: Salo Comercial No Distrito Industrial Campo Verde, Salo Comercial, M2/ Terreno: 5.000, M2 rea Construida: 3.000, Detalhes: tima Localizao Com Asfalto Para Rua, Estacionamento Para 60 Carros.barraco 5.000m2c/ Escritrio Isolado Ao Lado do Galpo C/ 6 Salas Grandes Von Livre Cond. Port.24h.\",Ref.: SA95950 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial Campo Verde,https://thumbs4.properati.com/6/7lTVZ92lzm8ap5amDGM_eb1EHOU=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2528/yS1CaXFW8XYwJdPuhgGn.jpg\n2ee8e3818d61fdc4e01a5d89be181974d74e4380,2014-08-15,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8424141,-49.3549387\",-20.8424141,-49.3549387,15000,BRL,11860.02,3840.93,,,,,,,,http://www.properati.com.br/mqtc_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95109Tipo: Salo Comercial, Salo IndustrialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Tancredo Neves .Terrea M2/ Terreno: 2.100, M2 rea Construida: 936, Detalhes: Estacionamento Na Frente,entrada Lateral Para Carga e Descarga,prox. do Carrefour\",\"Tipo: Salo Comercial, Salo Industrial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial Tancredo Neves .\",https://thumbs4.properati.com/9/KVlFV9eR-3HhzpzA5EtDKJICEgY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1358/k6zhNAyNgH9P1excuuTM.jpg\n0f0577616ba85404705aaead7f35ff6e8b4a91da,2014-08-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5733308,-46.544242\",-23.5733308,-46.544242,15000,BRL,11860.02,3840.93,,,,,,,,http://www.properati.com.br/mt62_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: CO95825 Tipo: Predio Comercial, Salo Comercial, Salo Industrial Cidade: So Jos do Rio Preto - SP Bairro: Pq. IndustrialWc. Social: 02, Salas : 06, M2/ Terreno: 1.113, Documentao: Em Dia., Detalhes: Banheiro Feminino e Masculino, 06 Salas Amplas, Sendo de 420 M a Maior.o Salo Possui Ambientes Para Reunies, Sala de Refeitrio de 93 M e Armrio Embutido, Alarme, Campainha, Interfone, Cerca Eltrica e Estacionamento de 258 M Com 25 Vagas Cobertas.\",\"Tipo: Predio Comercial, Salo Comercial, Salo Industrial Cidade: So Jos do Rio Preto - SP Bairro: Pq. Industrial\",https://thumbs4.properati.com/5/ug_ZifuqqZGKLp9_hQAm2sSvkgE=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1436/Q14vgp5W0wZIU71vOSui.jpg\n8d6a13266a268bd8dd46f19df78ef71e3509ee5a,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7700326,-49.3956468\",-20.7700326,-49.3956468,6500,BRL,5139.33,1664.4,,,,,,,,http://www.properati.com.br/ri3n_alugar_comercial_sao-jose-do-rio-preto_avenida-ada-marzochi-polachini,\"Ref.: SA95917Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Mini Dist. Industrial Adail Vetorasso..: Salo Comercial No Mini Destrito Industrial Adail Vetorasso, Salo Comercial, Wcs.: 02, Cozinha: 01, Escritrio, M2/ Terreno: 1.050, M2 rea Construida: 680, Detalhes: Barraco Novo, Com Pilar No Centro;porta Na Frente e Lateral;cobertura Metlica;p Direito 6 Metros.\",Ref.: SA95917 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Mini Dist. Industrial Adail Vetorasso,https://thumbs4.properati.com/2/3gLCbr6emuqSINA3fAYUW1zaIUM=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2283/grmYrDDM8HnJtcT4RzOD.jpg\ndac06563fc851c6596496ceaff68cbf177bb6dde,2014-10-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7863702,-49.3690389\",-20.7863702,-49.3690389,6500,BRL,5139.33,1664.4,,,,,,,,http://www.properati.com.br/qz9p_alugar_comercial_sao-jose-do-rio-preto_avenida-solon-da-silva-varginha,\"Ref.: SA95834Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Vila NovaesWc. Social: 03, Mesanino, M2/ Terreno: 530, M2 rea Construida: 480, Detalhes: Salo Comercial Com P Direito de 6m e Mezanino de 55 M.possui Aparelhagem de Ar Condicionado, Som Ambiente, Cmeras de Segurana e Estacionamento Para 18 Carros.local Ideal Para Lojas, Mercados, Igreja, Depsito, Entre Outros.\",Ref.: SA95834 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Vila Novaes,https://thumbs4.properati.com/8/qD9kCsVbiYyB0WGFw5WesI2Ny6s=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2092/d62f4d68-2ea5-4f83-bde9-fdc53bb504f2.jpg\n1ccba6f23edef69215ca9cfe74214a5276597714,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,6500,BRL,5139.33,1664.4,,,,,,,,http://www.properati.com.br/ri3q_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95919Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Centenrio da Emancipao..: Salo Comercial No Distrito Industrial Centenrio da Emancipao., Salo Comercial, Cozinha: 02, Mesanino, Escritrio, M2 rea Construida: 1.015, Detalhes: Escritrio, Cozinha, 02 Wc, Mezanino, 2 Salas, Entrada Lateral.salo 700 Metrosdiviso 315 Metros.\",Ref.: SA95919 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial Centenrio da Emancipao,https://thumbs4.properati.com/0/FlEAiRaerT0pRZEeW7AhhCuJjJc=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2286/v22UnUeHl8YbhJaP8IaU.jpg\na9d04cd869b56cac7bcb3a54a27147996a973a3d,2015-01-20,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5418835,-46.5583192\",-23.5418835,-46.5583192,1900,BRL,1539.57,498.6,,,,,16,1,,http://www.properati.com.br/w730_alugar_apartamentos_tatuape,\"APTO DE 58M/2, COM 02 DORMITRIOS, SENDO UMA SUTE, SALA AMPLA COM SACADA, COZINHA, WC SOCIAL, REA DE SERVIO, COM ALGUMAS MOBILIAS, TUDO DE PRIMEIRA, 02 VAGAS DE GARAGEM, LAZER COMPLETE, EXCELENTE LOCALIZAO, A UMA QUADRA DO METR.\",MOBILIADO EXCLUSIVE,https://thumbs4.properati.com/2/KuQXATs4BGrSaZvfCzOSMN2pqWs=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/19/30ad0c15-c9ef-48f7-a06e-2a0201bb7433.jpg\n5de8d0e3c8872b0af03bdc517a4a12021c48da59,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,24700,BRL,23430.5,7588.09,,289,26.25636678200692,85.46712802768167,,,,http://www.properati.com.br/ifvd_alugar_comercial_vila-olimpia,\"EDIFí�CIO COMERCIAL DE ALTí�SSIMO PADRí�O,  LOCALIZADO NO PONTO MAIS NOBRE DA REGIí�O, EM FRENTE AOSHOPPING VILA OLí�MPIA, ENTRE AV. DOS BANDEIRANTES E JUSCELINO KUBITSCHEK. OPRí�DIO CONTA COM 6 ELEVADORES DE ALTA VELOCIDADE, SENDO 1 PANORí�MICO, ARCONDICIONADO CENTRAL, FACHADA EM CORTINA DE VIDRO. EDIFí�CIO INTELIGENTE COMSOFISTICADO SISTEMA DE CONTROLE DE INCí�NDIO, CONSUMO DE ENERGIA. Referíªncia: CO66\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",\n705fdcf676aca2c9f51fcc0a332fcff39ee1706c,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,19500,BRL,18497.74,5990.59,,260,23.04073076923077,75,,,,http://www.properati.com.br/ifyk_alugar_comercial_vila-olimpia,\"\tConjunto Comercial com 260m² de área útil, 6 vagas, Ar condicionado Central. Referíªncia: CO200\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/0/-wzyHwNVYSdVIvzDSj7dz-RtyfY=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/200/f2ef8797-b051-4b62-b4fe-e8fdb7523a26.jpg\n179ce00d44c6fafbac93e139f052ab4ce304100d,2014-08-04,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,4200,BRL,3320.8,1075.46,,,,,,,,http://www.properati.com.br/lxpa_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95877 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Village La MontangneDormitrios: 3, Suite: 1, C/ Hidro: 1, Tipo Apto.: 2, C/ A/e: 3, Cozinha Planejada, Escritrio, Lavabo, Salas : 1, Sala P/ Ambientes : 3, Piscina, rea de Servio, M2/ Terreno: 360, M2 rea Construida: 250, Garagem Descoberta: 2, Garagem Coberta: 2\",Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Village La Montangne,https://thumbs4.properati.com/8/XTvYImGZcecKNC8xwzjnrrqWibg=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/804/ce485ed8-97e3-4af6-bc5e-3d347e06b31d.jpg\n868d3ff8ea898557e9819638705d2f823431d000,2015-01-13,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.521407,-46.5769202\",-23.521407,-46.5769202,1500,BRL,1215.45,393.63,,,,,13,1,,http://www.properati.com.br/vysr_alugar_apartamentos_tatuape,\"APTO DE 60M/2, COM 02 DORMITRIO, SALA AMPLA COM SACADA, WC SOCIAL, COZINHA, REA DE SERVIO, 01 VAGA DE GARAGEM, EXCELENTE ESTRUTURA DE LAZER COM PISCINA, PLAYGROUD, SALO DE FESTA, CHURRASQUEIRA, EXCELENTE LOCALIZAO, FCIL ACESSO AO SHOPPING METR/TATUAP.\",131 VITORIA,https://thumbs4.properati.com/0/yyioUci-ExAIuKlfIudXKNKaMAs=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/16/ff90ae98-dbde-47dc-9f04-32cdb2d2d0d3.jpg\nb9de26b4b13ad0f58469677b72b5171c2b04ae1f,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,13500,BRL,12806.12,4147.33,,183,22.663005464480875,73.77049180327869,,,,http://www.properati.com.br/ifv7_alugar_comercial_vila-olimpia,CONJUNTO COMERCIAL - RECEPCAO - HD - 4 SALAS - 2 WC - COPA  Referíªncia: CO60,\"Comercio no(a) Vila Olímpia , São Paulo - SP\",\n416be9056b69e83819532b65a04c6d1f7135f1ca,2014-12-11,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2400,BRL,1897.57,614.54,,,,,,,,http://www.properati.com.br/uict_alugar_comercial_jardim-maracana,\"Ref.: SA95945Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Jardim Maracan..: Salo Comercial No Jardim Maracan, Salo Comercial, Wc. Social: 02, Cozinha: 01, Forro de Laje, Piso Salas: Frio, M2/ Terreno: 225, M2 rea Construida: 130\",Ref.: SA95945 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Jardim Maracan,https://thumbs4.properati.com/5/3964ltfTag6YXQlyxEM19akYy1s=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2500/f25a8d90-6759-426c-a4ed-d8aefc6b19a4.jpg\nc1aff8edd38c3254c5775927802a8b8fae45a1d8,2014-08-05,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2400,BRL,1897.57,614.54,,,,,,1,,http://www.properati.com.br/mel5_alugar_casas_bosque-da-saude,\"Ref.: CA95883Tipo: Casa ResidencialCidade: So Jos do Rio Preto - SPBairro: Bosque Da Saude..: Casa Residencial No Bosque da Saude, Casa Residencial, Dormitrios: 03, Tipo Apto.: 01, C/ A/e: 03, Wc. Social: 01, Copa: 01, Cozinha: 01, Despensa, Salas : 02, Varanda, Garagem Descoberta: 02, Garagem Coberta: 02\",Tipo: Casa Residencial Cidade: So Jos do Rio Preto - SP Bairro: Bosque Da Saude,https://thumbs4.properati.com/0/aAdm951DKitDcqkiJ8p68O6xdNA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/848/083f9a71-b22a-4897-bf14-72fbe585dea5.jpg\ne4d569301fb8a21047d67f193929ecf360e9405e,2015-01-11,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,750,BRL,600.6,194.51,,,,,,1,,http://www.properati.com.br/vxnv_alugar_apartamentos_jardim-satelite,\"Excelente apartamento em frente ao condomnio Quinta das Flores totalmente reformado com 2 dormitrios, sala, cozinha planejada, rea de servio, lavabo com gabinete, wc com box blindex e 1 vaga de garagem.Ideal para casal sem filhos e sem animais\",Excelente apartamento no Jardim Satlite,https://thumbs4.properati.com/1/RhUtbG6YnK8FCa_yOduiEf3yyj0=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/tyrreno/photos/1074/71219c81-463f-4710-a46d-5e7e2fee9362.jpg\n6b59e27767f7570db49b2175077b1c059fa20b88,2014-10-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5715766,-46.6697673\",-23.5715766,-46.6697673,3100,BRL,2451.06,793.79,,,,,,,,http://www.properati.com.br/qz9r_alugar_comercial_sao-jose-do-rio-preto_avenida-brasil,\"Ref.: SA95828Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Jardim MugnaniWc. Social: 02, Cozinha, M2/ Terreno: 350, M2 rea Construida: 210, Detalhes: - Salo Novo!\", Ref.: SA95828 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Jardim Mugnani,https://thumbs4.properati.com/5/ShU0-2rvOmZQkWG3Ml57SSATz_s=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2094/VmmbZZ9w1FykW0ldwpGM.jpg\n3931096ab81c0a74496858654375447e859df8f2,2014-12-05,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7910821,-49.3781115\",-20.7910821,-49.3781115,880,BRL,695.77,225.33,,,,,,1,,http://www.properati.com.br/ue04_alugar_apartamentos_sao-jose-do-rio-preto_rua-pedro-mulati,\"Ref.: AP95815Tipo: ApartamentoCidade: So Jos do Rio Preto - SPBairro: Jardim ConceioCobertura, Dormitrios: 02, C/ A/e, Wc. Social, Box Wcs., Sala de Estar, Cozinha, Piscina, rea de Servio, rea de Lazer, Garagem Coberta: 01, rea Util M2: 65, Playground\",Ref.: AP95815 Tipo: Apartamento Cidade: So Jos do Rio Preto - SP Bairro: Jardim Conceio,https://thumbs4.properati.com/3/0zqbkQ73LUnQeQMp0yntvzoZrq8=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2458/Dv0976z6fjHNbJf7oAX0.jpg\nc15e079e557ec6ad9d86baede07324e678686764,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,3800,BRL,3604.66,1167.39,,88,13.265795454545456,43.18181818181818,,,,http://www.properati.com.br/ifzr_alugar_comercial_saude,\"\tConjunto Comercial proximo do Metrô São Judas, com 76m², 2 banheiros, 2 vagas, 1 copa, predio 24 horas. Referíªncia: CO243\",\"Comercio no(a) Saúde , São Paulo - SP\",https://thumbs4.properati.com/4/h5_FesPCfkGnmz5ac8c3DvXy_cE=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/243/68c5314b-39b1-4ebd-af49-694d915540b0.jpg\n62cbd166c6e120af08202b4db367db83240ed7e1,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,8500,BRL,8063.11,2611.28,,300,8.704266666666667,28.333333333333332,,,,http://www.properati.com.br/ifw2_alugar_comercial_vila-olimpia,\"\t\t\tLindo(a) Comercio de 300 metros quadrados no bairro Vila Olímpia na cidade de São Paulo - SP.\t\t\t, 2 banheiro(s), 6 vagas(s) de garagem.\t \t\tCondições de Pagamento: \t \t \t \t\tCaracteristicas: Referíªncia: CO93\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/1/gH6VwXVOHsfAy5JV8elaJg59lGA=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/93/4276fcba-994a-4296-9a35-65c580711457.jpg\n5a5bfb194fdbec1a633f5894a66c8bf45c8ae463,2015-01-06,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,1100,BRL,869.7,281.66,,,,,,,,http://www.properati.com.br/vv2s_alugar_apartamentos_sao-jose-do-rio-preto,\"Ref.: AP95935Tipo: ApartamentoCidade: So Jos do Rio Preto - SPBairro: Vila Itlia..: Apartamento Residencial Na Vila Itlia, Apartamento, rea Util M2: 63, Dormitrios: 02, Closet , Tipo Apto.: 01, Wc. Social: 01, Sala de Tv, Cozinha: 01, Cozinha C/ A/e, Garagem Coberta: 01, Detalhes: Prdio Novo.\",Ref.: AP95935 Tipo: Apartamento Cidade: So Jos do Rio Preto - SP Bairro: Vila Itlia,https://thumbs4.properati.com/5/XPnfndqmnEKY04smTHRwdeanGd8=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2535/atPPaTZcVegFbIYjH45d.jpg\n8f0adde258e0b9ad5897ba55288b52005ca749a8,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,3200,BRL,3035.52,983.07,,100,9.8307,32,,,,http://www.properati.com.br/ifxp_alugar_comercial_republica,\"\tLoja/Galpão com mezanino, pé direito duplo, localizada na Av. Amaral Guegel, proximo da Consolação, 2 banheiro e 2 vagas demarcadas. Referíªncia: CO169\",\"Comercio no(a) República , São Paulo - SP\",https://thumbs4.properati.com/6/XJyKobKfgKlfwiO0SB2Tt2i0Y9w=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/169/50b1ac29-6a0b-4488-aaa4-07bc131dd969.jpg\n480229dcb811d916f90a02ffd9c5b62e8a72ccbe,2014-10-15,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8168432,-49.3930989\",-20.8168432,-49.3930989,17000,BRL,13441.34,4353.05,,,,,,,,http://www.properati.com.br/re16_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: CO95289Tipo: Predio ComercialCidade: So Jos do Rio Preto - SPBairro: Vila dos BancariosM2 rea Construida: 800, Garagem Descoberta: 20, Detalhes: Predio Comercial Com 10m Salas de 80 M Cada, Fica Em Um Pavimento Com Elevador, 20 Vagas Para Estacionar .\",Loja,https://thumbs4.properati.com/4/Z-A_sLSPQOFzDbPb8MkkcY9IwMU=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2239/bsfsEYepcv9Cu4EHDvjU.jpg\n\n", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "No pagination, no footer", - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "country_name": true, - "created_on": true, - "expenses": true, - "floor": true, - "geonames_id": true, - "id": true, - "image_thumbnail": true, - "lat": true, - "lat_lon": true, - "lon": true, - "operation": true, - "place_with_parent_names": true, - "price_aprox_local_currency": true, - "price_aprox_usd": true, - "state_name": true, - "surface_total_in_m2": true - }, - "indexByName": { - "country_name": 13, - "created_on": 9, - "currency": 19, - "description": 5, - "expenses": 24, - "floor": 7, - "geonames_id": 15, - "id": 0, - "image_thumbnail": 26, - "lat": 17, - "lat_lon": 16, - "lon": 18, - "operation": 8, - "place_name": 4, - "place_with_parent_names": 12, - "price": 11, - "price_aprox_local_currency": 20, - "price_aprox_usd": 21, - "price_per_m2": 23, - "price_usd_per_m2": 22, - "properati_url": 25, - "property_type": 2, - "rooms": 6, - "state_name": 14, - "surface_covered_in_m2": 3, - "surface_total_in_m2": 10, - "title": 1 - }, - "renameByName": { - "description": "Description", - "place_name": "City", - "price": "Price", - "properati_url": "URL", - "property_type": "Type", - "rooms": "Rooms", - "surface_covered_in_m2": "Surface", - "title": "Property name" - } - } - } - ], - "type": "table" + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "footer": { + "enablePagination": false, + "fields": "", + "reducer": ["sum"], + "show": false }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Surface" + } + ] + }, + "pluginVersion": "9.0.0-pre", + "targets": [ { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [ - { - "options": { - "rent": { - "color": "yellow", - "index": 0 - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "property_type" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background" - }, - { - "id": "mappings", - "value": [ - { - "options": { - "store": { - "color": "blue", - "index": 0 - } - }, - "type": "value" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "title" - }, - "properties": [ - { - "id": "custom.width", - "value": 205 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Number of rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 66 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Description" - }, - "properties": [ - { - "id": "custom.inspect", - "value": true - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "URL" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "Show details", - "url": "${__value.raw}" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Surface" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background-solid" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "dark-red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "yellow", - "value": 200 - }, - { - "color": "green", - "value": 300 - }, - { - "color": "#EAB839", - "value": 500 - }, - { - "color": "#6ED0E0", - "value": 1000 - }, - { - "color": "#EF843C", - "value": 2000 - } - ] - } - }, - { - "id": "unit", - "value": "areaM2" - }, - { - "id": "custom.width", - "value": 93 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Type" - }, - "properties": [ - { - "id": "custom.width", - "value": 94 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "City" - }, - "properties": [ - { - "id": "custom.width", - "value": 120 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 77 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Price" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 8, - "y": 0 - }, - "id": 6, - "options": { - "footer": { - "enablePagination": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": true - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Surface" - } - ] - }, - "pluginVersion": "9.0.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A" - } - ], - "title": "No pagination, footer", - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "country_name": true, - "created_on": true, - "expenses": true, - "floor": true, - "geonames_id": true, - "id": true, - "image_thumbnail": true, - "lat": true, - "lat_lon": true, - "lon": true, - "operation": true, - "place_with_parent_names": true, - "price_aprox_local_currency": true, - "price_aprox_usd": true, - "state_name": true, - "surface_total_in_m2": true - }, - "indexByName": { - "country_name": 13, - "created_on": 9, - "currency": 19, - "description": 5, - "expenses": 24, - "floor": 7, - "geonames_id": 15, - "id": 0, - "image_thumbnail": 26, - "lat": 17, - "lat_lon": 16, - "lon": 18, - "operation": 8, - "place_name": 4, - "place_with_parent_names": 12, - "price": 11, - "price_aprox_local_currency": 20, - "price_aprox_usd": 21, - "price_per_m2": 23, - "price_usd_per_m2": 22, - "properati_url": 25, - "property_type": 2, - "rooms": 6, - "state_name": 14, - "surface_covered_in_m2": 3, - "surface_total_in_m2": 10, - "title": 1 - }, - "renameByName": { - "description": "Description", - "place_name": "City", - "price": "Price", - "properati_url": "URL", - "property_type": "Type", - "rooms": "Rooms", - "surface_covered_in_m2": "Surface", - "title": "Property name" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [ - { - "options": { - "rent": { - "color": "yellow", - "index": 0 - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "property_type" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background" - }, - { - "id": "mappings", - "value": [ - { - "options": { - "store": { - "color": "blue", - "index": 0 - } - }, - "type": "value" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "title" - }, - "properties": [ - { - "id": "custom.width", - "value": 205 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Number of rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 66 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Description" - }, - "properties": [ - { - "id": "custom.inspect", - "value": true - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "URL" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "Show details", - "url": "${__value.raw}" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Surface" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background-solid" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "dark-red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "yellow", - "value": 200 - }, - { - "color": "green", - "value": 300 - }, - { - "color": "#EAB839", - "value": 500 - }, - { - "color": "#6ED0E0", - "value": 1000 - }, - { - "color": "#EF843C", - "value": 2000 - } - ] - } - }, - { - "id": "unit", - "value": "areaM2" - }, - { - "id": "custom.width", - "value": 93 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Type" - }, - "properties": [ - { - "id": "custom.width", - "value": 94 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "City" - }, - "properties": [ - { - "id": "custom.width", - "value": 120 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 77 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Price" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 16, - "y": 0 - }, - "id": 9, - "options": { - "footer": { - "enablePagination": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": false, - "sortBy": [ - { - "desc": true, - "displayName": "Surface" - } - ] - }, - "pluginVersion": "9.0.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A" - } - ], - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "country_name": true, - "created_on": true, - "expenses": true, - "floor": true, - "geonames_id": true, - "id": true, - "image_thumbnail": true, - "lat": true, - "lat_lon": true, - "lon": true, - "operation": true, - "place_with_parent_names": true, - "price_aprox_local_currency": true, - "price_aprox_usd": true, - "state_name": true, - "surface_total_in_m2": true - }, - "indexByName": { - "country_name": 13, - "created_on": 9, - "currency": 19, - "description": 5, - "expenses": 24, - "floor": 7, - "geonames_id": 15, - "id": 0, - "image_thumbnail": 26, - "lat": 17, - "lat_lon": 16, - "lon": 18, - "operation": 8, - "place_name": 4, - "place_with_parent_names": 12, - "price": 11, - "price_aprox_local_currency": 20, - "price_aprox_usd": 21, - "price_per_m2": 23, - "price_usd_per_m2": 22, - "properati_url": 25, - "property_type": 2, - "rooms": 6, - "state_name": 14, - "surface_covered_in_m2": 3, - "surface_total_in_m2": 10, - "title": 1 - }, - "renameByName": { - "description": "Description", - "place_name": "City", - "price": "Price", - "properati_url": "URL", - "property_type": "Type", - "rooms": "Rooms", - "surface_covered_in_m2": "Surface", - "title": "Property name" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [ - { - "options": { - "rent": { - "color": "yellow", - "index": 0 - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "property_type" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background" - }, - { - "id": "mappings", - "value": [ - { - "options": { - "store": { - "color": "blue", - "index": 0 - } - }, - "type": "value" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "title" - }, - "properties": [ - { - "id": "custom.width", - "value": 205 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Number of rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 66 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Description" - }, - "properties": [ - { - "id": "custom.inspect", - "value": true - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "URL" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "Show details", - "url": "${__value.raw}" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Surface" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background-solid" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "dark-red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "yellow", - "value": 200 - }, - { - "color": "green", - "value": 300 - }, - { - "color": "#EAB839", - "value": 500 - }, - { - "color": "#6ED0E0", - "value": 1000 - }, - { - "color": "#EF843C", - "value": 2000 - } - ] - } - }, - { - "id": "unit", - "value": "areaM2" - }, - { - "id": "custom.width", - "value": 93 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Type" - }, - "properties": [ - { - "id": "custom.width", - "value": 94 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "City" - }, - "properties": [ - { - "id": "custom.width", - "value": 120 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 77 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Price" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 8 - }, - "id": 7, - "options": { - "footer": { - "enablePagination": true, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Surface" - } - ] - }, - "pluginVersion": "9.0.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A" - } - ], - "title": "Pagination, no footer", - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "country_name": true, - "created_on": true, - "expenses": true, - "floor": true, - "geonames_id": true, - "id": true, - "image_thumbnail": true, - "lat": true, - "lat_lon": true, - "lon": true, - "operation": true, - "place_with_parent_names": true, - "price_aprox_local_currency": true, - "price_aprox_usd": true, - "state_name": true, - "surface_total_in_m2": true - }, - "indexByName": { - "country_name": 13, - "created_on": 9, - "currency": 19, - "description": 5, - "expenses": 24, - "floor": 7, - "geonames_id": 15, - "id": 0, - "image_thumbnail": 26, - "lat": 17, - "lat_lon": 16, - "lon": 18, - "operation": 8, - "place_name": 4, - "place_with_parent_names": 12, - "price": 11, - "price_aprox_local_currency": 20, - "price_aprox_usd": 21, - "price_per_m2": 23, - "price_usd_per_m2": 22, - "properati_url": 25, - "property_type": 2, - "rooms": 6, - "state_name": 14, - "surface_covered_in_m2": 3, - "surface_total_in_m2": 10, - "title": 1 - }, - "renameByName": { - "description": "Description", - "place_name": "City", - "price": "Price", - "properati_url": "URL", - "property_type": "Type", - "rooms": "Rooms", - "surface_covered_in_m2": "Surface", - "title": "Property name" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [ - { - "options": { - "rent": { - "color": "yellow", - "index": 0 - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "property_type" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background" - }, - { - "id": "mappings", - "value": [ - { - "options": { - "store": { - "color": "blue", - "index": 0 - } - }, - "type": "value" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "title" - }, - "properties": [ - { - "id": "custom.width", - "value": 205 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Number of rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 66 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Description" - }, - "properties": [ - { - "id": "custom.inspect", - "value": true - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "URL" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "Show details", - "url": "${__value.raw}" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Surface" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background-solid" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "dark-red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "yellow", - "value": 200 - }, - { - "color": "green", - "value": 300 - }, - { - "color": "#EAB839", - "value": 500 - }, - { - "color": "#6ED0E0", - "value": 1000 - }, - { - "color": "#EF843C", - "value": 2000 - } - ] - } - }, - { - "id": "unit", - "value": "areaM2" - }, - { - "id": "custom.width", - "value": 93 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Type" - }, - "properties": [ - { - "id": "custom.width", - "value": 94 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "City" - }, - "properties": [ - { - "id": "custom.width", - "value": 120 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 77 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Price" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 8 - }, - "id": 8, - "options": { - "footer": { - "enablePagination": true, - "fields": "", - "reducer": [ - "sum" - ], - "show": true - }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Surface" - } - ] - }, - "pluginVersion": "9.0.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A" - } - ], - "title": "Pagination and footer", - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "country_name": true, - "created_on": true, - "expenses": true, - "floor": true, - "geonames_id": true, - "id": true, - "image_thumbnail": true, - "lat": true, - "lat_lon": true, - "lon": true, - "operation": true, - "place_with_parent_names": true, - "price_aprox_local_currency": true, - "price_aprox_usd": true, - "state_name": true, - "surface_total_in_m2": true - }, - "indexByName": { - "country_name": 13, - "created_on": 9, - "currency": 19, - "description": 5, - "expenses": 24, - "floor": 7, - "geonames_id": 15, - "id": 0, - "image_thumbnail": 26, - "lat": 17, - "lat_lon": 16, - "lon": 18, - "operation": 8, - "place_name": 4, - "place_with_parent_names": 12, - "price": 11, - "price_aprox_local_currency": 20, - "price_aprox_usd": 21, - "price_per_m2": 23, - "price_usd_per_m2": 22, - "properati_url": 25, - "property_type": 2, - "rooms": 6, - "state_name": 14, - "surface_covered_in_m2": 3, - "surface_total_in_m2": 10, - "title": 1 - }, - "renameByName": { - "description": "Description", - "place_name": "City", - "price": "Price", - "properati_url": "URL", - "property_type": "Type", - "rooms": "Rooms", - "surface_covered_in_m2": "Surface", - "title": "Property name" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [ - { - "options": { - "rent": { - "color": "yellow", - "index": 0 - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "property_type" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background" - }, - { - "id": "mappings", - "value": [ - { - "options": { - "store": { - "color": "blue", - "index": 0 - } - }, - "type": "value" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "title" - }, - "properties": [ - { - "id": "custom.width", - "value": 205 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Number of rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 66 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Description" - }, - "properties": [ - { - "id": "custom.inspect", - "value": true - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "URL" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "Show details", - "url": "${__value.raw}" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Surface" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background-solid" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "dark-red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "yellow", - "value": 200 - }, - { - "color": "green", - "value": 300 - }, - { - "color": "#EAB839", - "value": 500 - }, - { - "color": "#6ED0E0", - "value": 1000 - }, - { - "color": "#EF843C", - "value": 2000 - } - ] - } - }, - { - "id": "unit", - "value": "areaM2" - }, - { - "id": "custom.width", - "value": 93 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Type" - }, - "properties": [ - { - "id": "custom.width", - "value": 94 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "City" - }, - "properties": [ - { - "id": "custom.width", - "value": 120 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 77 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Price" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 0, - "y": 16 - }, - "id": 10, - "options": { - "footer": { - "enablePagination": true, - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": false, - "sortBy": [ - { - "desc": true, - "displayName": "Surface" - } - ] - }, - "pluginVersion": "9.0.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A" - } - ], - "title": "Pagination, no footer, no header", - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "country_name": true, - "created_on": true, - "expenses": true, - "floor": true, - "geonames_id": true, - "id": true, - "image_thumbnail": true, - "lat": true, - "lat_lon": true, - "lon": true, - "operation": true, - "place_with_parent_names": true, - "price_aprox_local_currency": true, - "price_aprox_usd": true, - "state_name": true, - "surface_total_in_m2": true - }, - "indexByName": { - "country_name": 13, - "created_on": 9, - "currency": 19, - "description": 5, - "expenses": 24, - "floor": 7, - "geonames_id": 15, - "id": 0, - "image_thumbnail": 26, - "lat": 17, - "lat_lon": 16, - "lon": 18, - "operation": 8, - "place_name": 4, - "place_with_parent_names": 12, - "price": 11, - "price_aprox_local_currency": 20, - "price_aprox_usd": 21, - "price_per_m2": 23, - "price_usd_per_m2": 22, - "properati_url": 25, - "property_type": 2, - "rooms": 6, - "state_name": 14, - "surface_covered_in_m2": 3, - "surface_total_in_m2": 10, - "title": 1 - }, - "renameByName": { - "description": "Description", - "place_name": "City", - "price": "Price", - "properati_url": "URL", - "property_type": "Type", - "rooms": "Rooms", - "surface_covered_in_m2": "Surface", - "title": "Property name" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [ - { - "options": { - "rent": { - "color": "yellow", - "index": 0 - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "property_type" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background" - }, - { - "id": "mappings", - "value": [ - { - "options": { - "store": { - "color": "blue", - "index": 0 - } - }, - "type": "value" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "title" - }, - "properties": [ - { - "id": "custom.width", - "value": 205 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Number of rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 66 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Description" - }, - "properties": [ - { - "id": "custom.inspect", - "value": true - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "URL" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "Show details", - "url": "${__value.raw}" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Surface" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background-solid" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "dark-red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "yellow", - "value": 200 - }, - { - "color": "green", - "value": 300 - }, - { - "color": "#EAB839", - "value": 500 - }, - { - "color": "#6ED0E0", - "value": 1000 - }, - { - "color": "#EF843C", - "value": 2000 - } - ] - } - }, - { - "id": "unit", - "value": "areaM2" - }, - { - "id": "custom.width", - "value": 93 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Type" - }, - "properties": [ - { - "id": "custom.width", - "value": 94 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "City" - }, - "properties": [ - { - "id": "custom.width", - "value": 120 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 77 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Price" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 8, - "y": 16 - }, - "id": 11, - "options": { - "footer": { - "enablePagination": true, - "fields": "", - "reducer": [ - "sum" - ], - "show": true - }, - "showHeader": false, - "sortBy": [ - { - "desc": true, - "displayName": "Surface" - } - ] - }, - "pluginVersion": "9.0.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A" - } - ], - "title": "Pagination, footer, no header", - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "country_name": true, - "created_on": true, - "expenses": true, - "floor": true, - "geonames_id": true, - "id": true, - "image_thumbnail": true, - "lat": true, - "lat_lon": true, - "lon": true, - "operation": true, - "place_with_parent_names": true, - "price_aprox_local_currency": true, - "price_aprox_usd": true, - "state_name": true, - "surface_total_in_m2": true - }, - "indexByName": { - "country_name": 13, - "created_on": 9, - "currency": 19, - "description": 5, - "expenses": 24, - "floor": 7, - "geonames_id": 15, - "id": 0, - "image_thumbnail": 26, - "lat": 17, - "lat_lon": 16, - "lon": 18, - "operation": 8, - "place_name": 4, - "place_with_parent_names": 12, - "price": 11, - "price_aprox_local_currency": 20, - "price_aprox_usd": 21, - "price_per_m2": 23, - "price_usd_per_m2": 22, - "properati_url": 25, - "property_type": 2, - "rooms": 6, - "state_name": 14, - "surface_covered_in_m2": 3, - "surface_total_in_m2": 10, - "title": 1 - }, - "renameByName": { - "description": "Description", - "place_name": "City", - "price": "Price", - "properati_url": "URL", - "property_type": "Type", - "rooms": "Rooms", - "surface_covered_in_m2": "Surface", - "title": "Property name" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [ - { - "options": { - "rent": { - "color": "yellow", - "index": 0 - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "property_type" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background" - }, - { - "id": "mappings", - "value": [ - { - "options": { - "store": { - "color": "blue", - "index": 0 - } - }, - "type": "value" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "title" - }, - "properties": [ - { - "id": "custom.width", - "value": 205 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Number of rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 66 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Description" - }, - "properties": [ - { - "id": "custom.inspect", - "value": true - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "URL" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "Show details", - "url": "${__value.raw}" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Surface" - }, - "properties": [ - { - "id": "custom.displayMode", - "value": "color-background-solid" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "dark-red", - "value": null - }, - { - "color": "orange", - "value": 100 - }, - { - "color": "yellow", - "value": 200 - }, - { - "color": "green", - "value": 300 - }, - { - "color": "#EAB839", - "value": 500 - }, - { - "color": "#6ED0E0", - "value": 1000 - }, - { - "color": "#EF843C", - "value": 2000 - } - ] - } - }, - { - "id": "unit", - "value": "areaM2" - }, - { - "id": "custom.width", - "value": 93 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Type" - }, - "properties": [ - { - "id": "custom.width", - "value": 94 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "City" - }, - "properties": [ - { - "id": "custom.width", - "value": 120 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Rooms" - }, - "properties": [ - { - "id": "custom.width", - "value": 77 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Price" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 8, - "x": 16, - "y": 16 - }, - "id": 12, - "options": { - "footer": { - "enablePagination": true, - "fields": "", - "reducer": [ - "sum" - ], - "show": true - }, - "showHeader": false, - "sortBy": [ - { - "desc": true, - "displayName": "Surface" - } - ] - }, - "pluginVersion": "9.0.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A" - } - ], - "transformations": [ - { - "id": "organize", - "options": { - "excludeByName": { - "country_name": true, - "created_on": true, - "expenses": true, - "floor": true, - "geonames_id": true, - "id": true, - "image_thumbnail": true, - "lat": true, - "lat_lon": true, - "lon": true, - "operation": true, - "place_with_parent_names": true, - "price_aprox_local_currency": true, - "price_aprox_usd": true, - "state_name": true, - "surface_total_in_m2": true - }, - "indexByName": { - "country_name": 13, - "created_on": 9, - "currency": 19, - "description": 5, - "expenses": 24, - "floor": 7, - "geonames_id": 15, - "id": 0, - "image_thumbnail": 26, - "lat": 17, - "lat_lon": 16, - "lon": 18, - "operation": 8, - "place_name": 4, - "place_with_parent_names": 12, - "price": 11, - "price_aprox_local_currency": 20, - "price_aprox_usd": 21, - "price_per_m2": 23, - "price_usd_per_m2": 22, - "properati_url": 25, - "property_type": 2, - "rooms": 6, - "state_name": 14, - "surface_covered_in_m2": 3, - "surface_total_in_m2": 10, - "title": 1 - }, - "renameByName": { - "description": "Description", - "place_name": "City", - "price": "Price", - "properati_url": "URL", - "property_type": "Type", - "rooms": "Rooms", - "surface_covered_in_m2": "Surface", - "title": "Property name" - } - } - } - ], - "type": "table" + "csvContent": "\"id\",\"created_on\",\"operation\",\"property_type\",\"place_name\",\"place_with_parent_names\",\"country_name\",\"state_name\",\"geonames_id\",\"lat_lon\",\"lat\",\"lon\",\"price\",\"currency\",\"price_aprox_local_currency\",\"price_aprox_usd\",\"surface_total_in_m2\",\"surface_covered_in_m2\",\"price_usd_per_m2\",\"price_per_m2\",\"floor\",\"rooms\",\"expenses\",\"properati_url\",\"description\",\"title\",\"image_thumbnail\"\nf73c373e0588bf568164a057dd94203ac87d47b1,2014-08-07,rent,store,Catu,|Brasil|Bahia|Catu|,Brasil,Bahia,,,,,0,,0,0,,,,,,,,http://www.properati.com.br/mfye_alugar_outros_catu,\"Otimo Imovel com o melhor valor da regiao, confira as fotos - \",Exclusividade! - Loja Em Catu,https://thumbs4.properati.com/3/jA8isZw8Xju7z3-V2I-ED8x5D2o=/trim/198x0/smart/filters:strip_icc()/galeriadefotos.net.br/backup/upload/15989/1598966287bc7/zd533ba5e829b0543093a45e1d8398bd7.jpg\nd6d377927ab49671acdd38aa6f661a460601a88b,2015-01-20,rent,apartment,Pernambuco,|Brasil|Pernambuco|,Brasil,Pernambuco,,\"-8.126271,-34.903793\",-8.126271,-34.903793,1200,BRL,1184.32,383.55,,63,6.088095238095239,19.047619047619047,1,2,,http://www.properati.com.br/w71k_alugar_apartamentos_pernambuco,\"CONTATOS: Claro: 81./ Tim: 81./ Oi: 81./ Vivo: 81.Apartamento de 2 quartos mais Dependência completa de empregada, 1.º andar de edifício localizado na Rua Dr. Gaspar Regueira da Costa, 99, Boa Viagem.Este apartamento é constituído por: 1 Sala, 2 Quartos, 2 Banheiros, Cozinha, Área de serviço, Dependência completa de empregada e 1 lugar de estacionamento.O Valor do aluguer já inclui taxas.Agende já a sua visita.Nota: O Edifício irá ser pintado exteriormente, em breve.ISI-120-T2 #ref:ISI-120-T2 - ISI-120-T2 - idalécio Soares\",\"Apartamento de 2 quartos, DCE, 1.º Andar, 63 m2, Boa Viagem - Recife\",https://thumbs4.properati.com/0/B9BCwP1HK7NvZIYINGkFNnNfOZ4=/trim/198x0/smart/filters:strip_icc()/feedmedia.egorealestate.com/Zfeed/S5/C4123/P2270979/Tphoto/ID03a72200-0000-0500-0000-00000141a7a5.JPG\nc6826a9508436920fd59b46a202354f3e4823d22,2014-10-29,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.1546253,-45.7906242\",-23.1546253,-45.7906242,0,BRL,0,0,,3800,,,,,,http://www.properati.com.br/s41f_alugar_comercial_cacapava,\"Condomínio com 22 Módulos de Galpões; 3.800m2 a 12.600m2; 12m pé-direito médio; Piso 6 TON/m2; Ã�rea total de Terreno : 324.000 m2; Ã�rea Total construída: 133.200 m2; 293 vagas para automóveis; 103 vagas para caminhões; Refeitório para 256 pessoas por turno;Benefícios Fiscais no município de Caçapava conforme Lei Lei 5055/211; Isenção de Taxas;Isenção de IPTU; Redução de ISS; Reversão de ICMS.Valores locação = R$ 15,00 o m2Tx Cond. = R$ 1,50 o m2Valores poderão ser alterados sem prévio aviso.A Heron Imóveis oferece este(a) Comercial em Caçapava, no bairro Caçapava. Gostou desse(a) Comercial em Caçapava? Então veja mais informações! Acesse o nosso site clicando no botão Contate o anunciante ou ligue - ou ainda entre em contato conosco pelo email contato@ Obrigado!\",\"Comercial em Caçapava, no bairro Caçapava\",https://thumbs4.properati.com/1/J2n8nXVlvduy6EJjyLwPneEoc28=/trim/198x0/smart/filters:strip_icc()/www.heronimoveis.com.br/fotos/F00/35/23500-DSC06177.JPG\nba957ad3cb2d12d55d1f0466eeffe788a35a6f4f,2014-08-04,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7780086,-49.3450562\",-20.7780086,-49.3450562,950,BRL,751.1,243.25,,,,,,1,,http://www.properati.com.br/lxp9_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95876 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Garden Village II..:Casa Condomnio Garden Village II, Casa Condominio, Dormitrios: 03, Tipo Apto.: 01, Wc. Social: 01, Sala de Visita: 01, Copa: 01, Cozinha: 01, Cozinha Planejada, Despensa, rea de Servio, Ar Condicionado, Alarme, Acabamento Em Gesso, Sanca, Campainha, Cerca Eletrica, Forro de Laje, Piso Dormitrios: Frio, Piso Salas: Frio, M2/ Terreno: 145, M2 rea Construida: 70, Garagem Descoberta: 01, Salo de Festa, Detalhes: R$ 950,00 + Iptu + Condomnio\", Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Garden Village II,https://thumbs4.properati.com/9/xcrfHjSnXdhGcOK1m1wg1jHJlsw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/803/8860a8e8-6d62-49e6-a992-54ab8f360f35.jpg\n0999b33908c71af3b8be282ab35ab61b38a46353,2014-07-16,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,4300,BRL,4078.98,1321,,319,4.141065830721003,13.47962382445141,,4,,http://www.properati.com.br/ifuj_alugar_apartamentos_vila-olimpia,\"Apartamento na Vila Olí­mpia, com 4 quartos, sendo 2 suí­tes,Ampla sala com terraí§o, piso de madeira corrida, 1 dos quartos pode ser revertido para sala de tv ou escritório.Quartos com carpete de madeira, as suí­tes com armí¡rios embutidos, uma delas com ar condicionado.Armí¡rio corredor.Cozinha com armí¡rio, Piso de granito, na cozinha e lavanderia.  Referíªncia: AP34\",\"Apartamento no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/8/nxrbzTDipvKnqdx1wDABEyIbSes=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/34/c789b808-bf30-460d-9376-16299ce5abbb.jpg\n011d5618639ce229b012022a32b22eff9abd213e,2014-11-19,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.9648026,-46.3855723\",-23.9648026,-46.3855723,1300,BRL,1323.12,428.5,,60,7.141666666666667,21.666666666666668,4,1,,http://www.properati.com.br/tl4s_alugar_apartamentos_sao-paulo,\"PACOTE R$ 1.300,00 OTIMO APARTAMENTO MOBILIADO COM TERRAÇO PROXIMO SHOPPING , PRAIA E COMERCIOS ACEITA FIADOR / SEG. FIANÇA A 3 Milenio oferece este(a) Apartamento em Sao Vicente, no bairro Gonzaguinha - 1 dormitórios. Gostou desse(a) Apartamento em Sao Vicente? Então veja mais informações! Acesse o nosso site clicando no botão Contate o anunciante ou ligue - ou ainda entre em contato conosco pelo email vendas@ Obrigado!\",\"Apartamento em Sao Vicente, no bairro Gonzaguinha - 1 dormitórios\",https://thumbs4.properati.com/4/x8BqTbd304ntTBY5pTf5Yi_nNMw=/trim/198x0/smart/filters:strip_icc()/www.imobiliariasdesantos.com.br/fotos/F00/74/757400-FOTOS%20HELANE%20001.jpg\n6f274a2d23ffa89cb0301f3277585beda7723ce8,2014-09-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8343086,-49.3805559\",-20.8343086,-49.3805559,5000,BRL,3953.34,1280.31,,,,,,,,http://www.properati.com.br/o23s_alugar_comercial_sao-jose-do-rio-preto_rua-nuno-alvares-pereira,\"Ref.: SA95668Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Pq. EstorilM2 rea Construida: 170, Detalhes: Salo de 170 M, Com Mezanino. J Com Acessibilidade. Tem Uma Linda Fachada e Estacionamento Para Clientes.\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Pq. Estoril,https://thumbs4.properati.com/8/7VqZVGV6Hl3paOrhYXv_ksowyEw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1875/TG5yXGj9EJPaa5o0dWgL.jpg\ndb6f890847bcacc6ec3e78a0e6613132174e4c70,2014-12-05,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178411,-49.387605\",-20.8178411,-49.387605,10000,BRL,7906.68,2560.62,,,,,,1,,http://www.properati.com.br/ue07_alugar_casas_sao-jose-do-rio-preto_rua-cila,\"Ref.: CO95026Tipo: Casa Comercial, Predio ComercialCidade: So Jos do Rio Preto - SPBairro: RedentoraSobrado, Dormitrios: 4, Tipo Apto.: 4, C/ A/e: 4, A/e C/ Interior: 4, Wc. Social: 1, Copa/cozinha: 1, Cozinha C/ A/e: 1, Despensa, Salas : 1, Sala P/ Ambientes : 4, M2 rea Construida: 330, Entrada Lateral: 1, Piso Dormitrios: Frio\",\"Ref.: CO95026 Tipo: Casa Comercial, Predio Comercial Cidade: So Jos do Rio Preto - SP Bairro: Redentora\",https://thumbs4.properati.com/2/DNREjVLgpGPzTlv8aIvhpd7KZkI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2461/02ea33d3-3ebd-4139-a380-b60eb84c87d1.jpg\nf7d3243419c85cf439189481819f13cb22ab0498,2014-10-01,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8126181,-49.3404534\",-20.8126181,-49.3404534,3800,BRL,3004.52,973.03,,,,,,,,http://www.properati.com.br/qz8t_alugar_casas_sao-jose-do-rio-preto_avenida-miguel-damha,\"Ref.: CA95902Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha III..: Casa Condomnio Damha III, Casa Condominio, Dormitrios: 03, Suite: 01, Closet , Tipo Apto.: 02, C/ A/e: 02, Home, Copa/cozinha: 01, Cozinha Planejada, Piscina, Varanda, rea de Servio, Wc. de Empregada/servio, Forro de Laje, M2/ Terreno: 450, M2 rea Construida: 240, rea de Lazer, Garagem Descoberta: 02, Garagem Coberta: 02, Churrasqueira, Valor Condominio R$-: 420,00\",Ref.: CA95902 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha III,https://thumbs4.properati.com/8/cqu5q3tBJOddQ526fWf2puVwINI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2060/6064cf7e-f8ed-44f3-8a80-b6576de27d62.jpg\ncbfc4c495b3578c995363825a85a821217fab4ec,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,14000,BRL,11069.36,3584.87,,,,,,,,http://www.properati.com.br/ri3p_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95918Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Mini Dist. Industrial Adail Vetorasso..: Salo Comercial No Mini Distrito Industrial Adail Vetorasso, Salo Comercial, Wcs.: 02, Cozinha: 01, Escritrio, M2/ Terreno: 2.100, M2 rea Construida: 1.350, Detalhes: Cobertura Metlica.p Direito Com 7 Metros.corredor Lateral.\",Ref.: SA95918 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Mini Dist. Industrial Adail Vetorasso,https://thumbs4.properati.com/9/INPTa4YGuWfsekPfC8ez-rqd3PI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2285/Mhd9hMU2ZwU9YXAC9Hzi.jpg\nb4dab15f9d4411d0847d1871e0a62e107b492fdc,2014-08-02,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.8773135,-46.7592613\",-23.8773135,-46.7592613,28000,BRL,22138.72,7169.74,,,,,,,,http://www.properati.com.br/lud3_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95869Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Eng. Schimidt..: Salo Comercial Em Engenheiro Schimidt, Salo Comercial, Obs: Salo Ideal Para Transportadora Com Frente Para 03 Marginal, Esquina Com Rua Projetada 01 e Rodovia W. Luiz, M2/ Terreno: 5.500, M2 rea Construida: 2.200\",Ref.: SA95869 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Eng. Schimidt,https://thumbs4.properati.com/3/oJm3M_UtBn2PVRsZ2O4n48IvhuA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/780/60d090bc-4a05-4929-88ae-aca5b9873a76.jpg\nd3769eb1b41c780c83f2d176adae8ad9a81b7ba3,2014-10-08,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.818728,-49.4212608\",-20.818728,-49.4212608,16000,BRL,12650.68,4096.99,,,,,,,,http://www.properati.com.br/r6d9_alugar_comercial_sao-jose-do-rio-preto_avenida-joao-batista-vetorasso,\"Ref.: SA95810Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Waldemar VerdiM2/ Terreno: 5.000, M2 rea Construida: 1.400, Detalhes: Com Recepo e 8 Salas. P Direito Com 7 M.\",Salo,https://thumbs4.properati.com/6/GFkI3LbwZQAhB8USv5fTltHxz0E=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2179/QEWBOD1T8GCOjBi0H6WI.jpg\nf51aebdb554e9b5dfa16c3538d3376040f5f2b5c,2014-08-20,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,16000,BRL,12650.68,4096.99,,,,,,,,http://www.properati.com.br/np97_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95723Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel HaddadM2 rea Construida: 322, Obs: Salo Ideal Para Loja, 217m Terreo e 105m Mezanino. Estacionamento Na Frente.imvel Novo.\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad,https://thumbs4.properati.com/2/viy2bV22HhxBkY_449PiH9sLQPw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1548/9242514f-75f0-4bdc-a5b1-68a958cbda17.jpg\n7266f270625a52c84adaf69ac08a53b397617247,2014-09-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,6000,BRL,4744,1536.37,,,,,,,,http://www.properati.com.br/o20x_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95742Tipo: Salo IndustrialCidade: So Jos do Rio Preto - SPBairro: Quinta das PaineirasWcs.: 02, Cozinha: 01, M2/ Terreno: 720, M2 rea Construida: 458,90, Obs: Salo Industrial Grande Com 458m, Com Boa Acessibilidade e tima Localizao, 02 Quadras da Avenida Potirendaba, Fcil Acesso e Entrada Para Caminhes.\",Tipo: Salo Industrial Cidade: So Jos do Rio Preto - SP Bairro: Quinta das Paineiras,https://thumbs4.properati.com/3/0rnMsn1G6rWTjCQ9lnrLn6WR7JA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1771/KET9NdQ0WNrtQlhGEVEq.jpg\nc1e10a8afe111ba5caa5034df1ecb56b25b772f9,2014-08-19,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,12000,BRL,9488,3072.74,,,,,,,,http://www.properati.com.br/muvz_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95766Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel HaddadWc. Social: 04, M2/ Terreno: 980, M2 rea Construida: 500, Obs: Entrada Lateral Com Acesso Para Caminho, Com 04 Wcs. Sendo 02 Wc P/ Deficiente.\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad,https://thumbs4.properati.com/3/GvRImi73Q9koPTqVulTG5pg1J2s=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1519/T181YBWZd62P9tMfgs1F.jpg\nd156f7cc95141ef1fca4de81d6dc65b143c9988f,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,12000,BRL,9488,3072.74,,,,,,,,http://www.properati.com.br/muwd_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95749Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel HaddadM2 rea Construida: 280, Obs: Salo Ideal Para Loja, 217mterreo + 68m Mezanino.estacionamento Na Frente,\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad,https://thumbs4.properati.com/5/A5TB5m6yLugDjIdQVsXy9br70YQ=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1533/eeec7b18-daa6-422b-aac1-82894f366e60.jpg\n90ed612641d0db18967ee9c07edc8b0647c97976,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,12000,BRL,9488,3072.74,,,,,,,,http://www.properati.com.br/muwc_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95725Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel HaddadM2 rea Construida: 280, Obs: Salo Ideal Para Loja, 217m Terreo, + 68m Mezanino.estacionamento Na Frente, Imvel Novo\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad,https://thumbs4.properati.com/2/8rbyOC1iGXRWRZqh5Z6pcOsdsfQ=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1532/a142f7a0-ff97-4794-84d8-4144253429c3.jpg\nc69e1ca4e7cd3346bc510240893bf2c2c7437e8e,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,12000,BRL,9488,3072.74,,,,,,,,http://www.properati.com.br/muvh_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95724Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel HaddadM2/ Terreno: 280, Obs: Salo Ideal Para Loja, 217m Terreo e + 68m Mezanino.Estacionamento Na Frente,imvel Novo.\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad,https://thumbs4.properati.com/1/3gyCoSFzrSme_NcQhKWIEnuoeaY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1501/CeAfrlKKrQIRE9uzNxoa.jpg\n0c5e391bac4d007585efb02466681c760a758423,2014-09-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8379345,-49.3370139\",-20.8379345,-49.3370139,8000,BRL,6325.32,2048.49,,,,,,,,http://www.properati.com.br/q4jv_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95899Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Ch. Estancia Jockey Clube..: Salo Comercial , Salo Comercial, Wcs.: 04, Wcs. C/ A/e, Cozinha: 01, Obs: P Direito 9 Metros, M2 rea Construida: 1.000, Detalhes: Salo Novo, Sendo Os Wcs Piso Porcelanato, Cozinha Porcelanato e Piso Liso, Com Telhado Vulcanizado.\",Ref.: SA95899 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Ch. Estancia Jockey Clube,https://thumbs4.properati.com/8/OM-MkRbrVgdcQa163wlgXjWDHqU=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2007/8541c9f9-4477-448d-a193-d2b00dc5eed4.jpg\n01bdf823f7827d745574bb7a180a39e4db359463,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,8000,BRL,6325.32,2048.49,,,,,,,,http://www.properati.com.br/muux_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95894Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Ch. Jockey Clube..: Salo Comercial Na Chcara Jockey Clube, Salo Comercial, M2/ Terreno: 5.000, M2 rea Construida: 1.150, Detalhes: Galpo Com Piso Cimento Polido, Estrutura Metlica, Entrada Lateral Para Caminho.p Direito Alto Com 6,/2 M. \",Ref.: SA95894 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Ch. Jockey Clube,https://thumbs4.properati.com/1/HZjM3Xdq2xWgpQ5gSrCVIASVnJA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1481/Tz9VYOVWXC1ZVU5fUHwL.jpg\n050a20b55332403b962575877b81d609a5988a15,2014-09-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8379345,-49.3370139\",-20.8379345,-49.3370139,8000,BRL,6325.32,2048.49,,,,,,,,http://www.properati.com.br/q4ju_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95900Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Ch. Estancia Jockey Clube..: Salo Comercial , Salo Comercial, Wcs.: 04, Wcs. C/ A/e, Cozinha: 01, Obs: P Direito 9 Metros, M2 rea Construida: 1.000, Detalhes: Salo Novo, Sendo Os Wcs Piso Porcelanato, Cozinha Porcelanato e Piso Liso, Com Telhado Vulcanizado.\",Ref.: SA95900 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Ch. Estancia Jockey Clube,https://thumbs4.properati.com/3/ZL10NlMLKOM5C49dt-MjpOTaaMo=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2006/9fbe5c67-00ba-4e7b-9904-08316f433dd4.jpg\nfeb178373d5eab7df3b6c546a5d1e6a192156c7f,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8457948,-49.353464\",-20.8457948,-49.353464,8000,BRL,6325.32,2048.49,,,,,,,,http://www.properati.com.br/muuv_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95892Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Ch. Jockey Clube..: Salo Comercial Na Chcara Jockey Clube, Salo Comercial, M2/ Terreno: 5.000, M2 rea Construida: 1.150, Detalhes: Galpo Com Piso Cimento Polido, Estrutura Metlica, Entrada Lateral Para Caminho.p Direito Alto Com 6,/2 M.\",Ref.: SA95892 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Ch. Jockey Clube ,https://thumbs4.properati.com/5/xwV26hopilS3ZK6xLE-O3iy4rE4=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1479/vT0mPYs7U2dAsti8LWGQ.jpg\n882a8542b44e551782d3126a1d43d907cdba854b,2014-08-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8442587,-49.3291753\",-20.8442587,-49.3291753,7000,BRL,5534.66,1792.43,,,,,,,,http://www.properati.com.br/mt5y_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95817Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Marginal Eng. SchimidtWc. Social: 02, Cozinha, Mesanino, Escritrio, M2/ Terreno: 2.000, M2 rea Construida: 1.100, Pintura Nova, Piso Dormitrios: Cimentado;, Piso Salas: Polido;, Detalhes: Salo Novo, Com Estrutura Metlica e Banheiro C/ Acessibilidade.\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Marginal Eng. Schimidt,https://thumbs4.properati.com/4/FPT3HYl7BCrZMA5Okj6iNxUgPRA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1432/MaEwnQ1cL6HLcrscPFb8.jpg\n201e0ccc9fc63a78f68391bdd74ac5407e07da9b,2014-08-04,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,1000,BRL,790.66,256.06,,,,,,1,,http://www.properati.com.br/lxpd_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95884Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Parque Liberdade III..:Casa Condomnio Parque da Liberdade III, Casa Condominio, Dormitrios: 03, C/ A/e: 02, Wc. Social: 01, Wcs. C/ A/e, Sala de Visita: 01, Cozinha: 01, Cozinha Planejada, Interfone, Acabamento Em Gesso, Sanca, Campainha, Forro de Laje, Obs: Imvel Semi Novo, Com Projeto da Arquiteta Kenia Miranda, o Imvel Tem Papel de Parede, Cortinas Nos Dormitrios e Sala, Luminrias Na Cozinha., M2/ Terreno: 143, Garagem Coberta: 02, Churrasqueira, Detalhes: R$ 1.000,00 + Iptu + Condominio\",Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Parque Liberdade III,https://thumbs4.properati.com/2/v319TrEqOc-5Hy9xmTQXh1KL2UE=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/807/7331be4a-3945-41cd-b761-4322696192f5.jpg\n05cf0728d4ba6016469b6b4a864542a1824dc25c,2014-08-15,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.7688712,-46.7100661\",-23.7688712,-46.7100661,2000,BRL,1581.32,512.12,,,,,,,,http://www.properati.com.br/mqtt_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95463Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Jardim So MarcosWc. Social: 2, Cozinha: 1, Mesanino, Recepo, M2/ Terreno: 406, M2 rea Construida: 110, Pintura Nova, Piso Salas: Piso Frio\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Jardim So Marcos,https://thumbs4.properati.com/9/EMUy3dbQiGwd8LmfHH92qsmCQ7Q=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1375/srbDQgpudCyI2lewxvjS.jpg\nf5942694fe76bfe705275acfe74148c588367f86,2014-09-01,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.7688712,-46.7100661\",-23.7688712,-46.7100661,4000,BRL,3162.64,1024.24,,,,,,8,,http://www.properati.com.br/o215_alugar_casas_sao-jose-do-rio-preto_rua-antonio-dias,\"Ref.: CO95681Tipo: Casa ComercialCidade: So Jos do Rio Preto - SPBairro: Jardim So MarcosWc. Social: 04, Sala(s): 08, M2/ Terreno: 280, Garagem Coberta: 02, Forro de Laje, Piso Salas: Frio, Iptu Valor: 58,00, Detalhes: timo Local Para Clincas.\",Tipo: Casa Comercial Cidade: So Jos do Rio Preto - SP Bairro: Jardim So Marcos,https://thumbs4.properati.com/4/F58YgAYlsu22NFL9RPPH5RURZJ8=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1779/sr0aS0NcXQGZ7HsOtyan.jpg\n1b093694ca05135b213162c0bd499e9adb708309,2014-12-10,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.6302071,-46.5818983\",-23.6302071,-46.5818983,4000,BRL,3162.64,1024.24,,,,,,,,http://www.properati.com.br/ufsq_alugar_comercial_sao-jose-do-rio-preto_avenida-marginal,\"Ref.: SA95942Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Pq. Res. Manor Daud..: Salo Comercial No Parque Residencial Manor Daud, Salo Comercial, Detalhes: Salo Com 340m, Entrada Para Caminho.frente Para Br153\",Ref.: SA95942 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Pq. Res. Manor Daud,https://thumbs4.properati.com/4/4e8bVPqS0g1ezrFKP9fcdzvSn0I=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2493/rJ3aboE1bZnwuqJHy4Lr.jpg\n50bd70c45a9a9b087c404cac6b67c491f64c211e,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2500,BRL,1976.65,640.15,,,,,,,,http://www.properati.com.br/ri3l_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95916Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Res. Caetano..: Salo Comercial No Residencial Caetano, Salo Comercial, Wcs.: 02, Cozinha: 01, M2 rea Construida: 350, Detalhes: Cozinha2 Wc Com Acessibilidade;1 Porta Na Frente1 Porta Na Lateral;barraco Novo.cobertura Com Estrutura Metlica. P Direito de 6 Metros.\",\"Ref.: SA95916 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Res. Caetano ..: Salo Comercial No Residencial Caetano, Salo Comercial, Wcs.: 02, Cozinha: 01, M2 rea Construida: 350, Detalhes: Cozinha 2 Wc Com Acessibilidade\",https://thumbs4.properati.com/9/l325GXwN3fD9xS0dglH5hmaB83A=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2281/FjKJBqBJFxhvJ4atJVuO.jpg\n50a4a96d3ec01b7b1032e5eec4a3496b38f35c4f,2014-10-09,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7780086,-49.3450562\",-20.7780086,-49.3450562,2500,BRL,1976.65,640.15,,,,,,,,http://www.properati.com.br/r9j7_alugar_casas_sao-jose-do-rio-preto_avenida-belvedere,\"Ref.: CA95904Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Village Damha Rio Preto I..: Casa Em Condomnio Village Damha Rio Preto I, Casa Condominio, Dormitrios: 03, Tipo Apto.: 01, C/ A/e: 03, Cozinha: 01, Cozinha Planejada, Varanda, rea de Servio, Wc. de Empregada/servio, Garagem Coberta: 02, Churrasqueira\",Ref.: CA95904 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Village Damha Rio Preto I,https://thumbs4.properati.com/1/CVM0xMvGlg_DX1BluR3vWpRkhMU=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2206/7f2a5d99-2465-48fb-859c-a52311ba0c18.jpg\nf5be311dcee4a66dcb26f463f76c49b6084de8ab,2014-11-11,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,1600,BRL,1411.74,457.2,,,,,2,1,,http://www.properati.com.br/t8nu_alugar_apartamentos_tatuape_rua-serra-de-braganca,\"APTO DE 40M/2 COM 01 DORMITRIO, SALA AMPLA COM SACADA, COZINHA PLANEJADA, WC SOCIAL, REA DE SERVIO, 01 VAGA DE GARAGEM LIVRE, LAZER COMPLETO COM PISCINA, PLAYGROUD, SALO DE FESTA, CHURRASQUEIRA, SAUNA, SALA DE FITINESS, SALA DE LEITURA, SALO DE JOGOS, EXCELENTE LOCALIZAO. O APTO ESTA COM FOGO, GELADEIRA, MICROONDAS, SUGGAR, BALCO ESTILO COZINHA AMERICANA, 02 BANQUETAS, 02 MESINHAS DE CANTO, 01 HOME-OFFICER, CAMA BOX COM COLCHO, VENTILADOR DE TETO, ARMRIO NO DORMITRIO.\",TOLEDO CONSULTORIA JURIDICA E IMOBILIARIA,https://thumbs4.properati.com/6/-9lTZIOGIGHwHaddpPoJu6s92hY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/2/6c5e21eb-1c7b-41d6-9257-b6e7994ea93c.jpg\na031b9b0ccce57678aab134025713cdb4ad7ed19,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7630911,-49.3810839\",-20.7630911,-49.3810839,1500,BRL,1185.99,384.09,,,,,,,,http://www.properati.com.br/ri3h_alugar_comercial_sao-jose-do-rio-preto_rua-gumercindo-tomas-de-aquino,\"Ref.: SA95914Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Centenrio da Emancipao..: Salo Comercial No Distrito Industrial Centenrio da Emancipao, Salo Comercial, Wcs.: 02, Cozinha: 01, M2/ Terreno: 200, M2 rea Construida: 100, Detalhes: Salo Em Construo, Com 2 Wc Com Acessibilidade e Cozinha.porta Frente e Lateral.estrutura Metlica, P Direito 5 Metros.\",Ref.: SA95914 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial Centenrio da Emancipao,https://thumbs4.properati.com/5/cuTILpWk120n1EtpTTP7CuHqFTI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2277/xOFbBdd0MAPLxuFIMMMc.jpg\n8198126233f30e45a7ec723640ecd09fc933f35f,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7630911,-49.3810839\",-20.7630911,-49.3810839,1500,BRL,1185.99,384.09,,,,,,,,http://www.properati.com.br/ri3j_alugar_comercial_sao-jose-do-rio-preto_rua-gumercindo-tomas-de-aquino,\"Ref.: SA95915Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Centenrio da Emancipao..: Salo Comercial No Distrito Industrial Centenrio da Emancipao, Salo Comercial, Wcs.: 02, Cozinha: 01, M2/ Terreno: 200, M2 rea Construida: 100, Detalhes: Salo Em Construo, Com 2 Wc Com Acessibilidade e Cozinha.porta Frente e Lateral.estrutura Metlica, P Direito 5 Metros.\",Ref.: SA95915 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial Centenrio da Emancipao,https://thumbs4.properati.com/0/LWklCEKjfuTI94hwh7U-tpkiQxw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2279/YBRlngu70KNBBuf3pXju.jpg\ncfe8aedbfe7496306c9ee598fdb0799d1a4d43a7,2014-08-14,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178411,-49.387605\",-20.8178411,-49.387605,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/mpsw_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95860Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: RedentoraSalo Comercial, Terrea, Acabamento Em Gesso, Forro de Laje, Piso Salas: Frio, M2 rea Construida: 160\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Redentora,https://thumbs4.properati.com/1/w-odT7rMEUYcr3TVS4fo2ausAAI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1278/Kp7YsojHwZdQDjK4JvMz.jpg\n9002dc7d32f4f097a4f9c789ad93b7f5304396cf,2014-08-14,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178411,-49.387605\",-20.8178411,-49.387605,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/mpsz_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95862Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: RedentoraM2 rea Construida: 190, Garagem Descoberta: 02, Forro de Laje, Piso Salas: Frio\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Redentora,https://thumbs4.properati.com/7/xW33i6TXrwRgMFrpyW_syKKKlPs=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1281/UKC5P4P7lZPyQSY9l8xC.jpg\n2128d47314a494ddecda6b6e981c46afbd3fae51,2014-10-30,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/s9we_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95921Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Redentora..: Salo Comercial Na Redentora, Salo Comercial, M2/ Terreno: 150, Detalhes: tima Localizao!\",Ref.: SA95921 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Redentora,https://thumbs4.properati.com/6/HVrSVpwKgIJpnnFxGcMzyHIeUx4=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2347/abTB8PxHKedvADO9yyhF.jpg\n0329fa7a5536c99eee743612010897cafed7f340,2014-10-15,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8030634,-49.4324803\",-20.8030634,-49.4324803,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/re15_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95910Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Res. Palestra..: Salo Comercial No Residencial Palestra, Salo Comercial, Wc. Social: 02, Forro de Laje, Obs: timo Salo, Novo., M2/ Terreno: 250, M2 rea Construida: 200\",Ref.: SA95910 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Res. Palestra,https://thumbs4.properati.com/8/MBuRdEvx5x-V-zpeycnxFNg-n70=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2238/NOqLX7BtA7KEH0SUs5rV.jpg\nc822a8819d8874916877cc6b3c91194cbf5968ca,2014-10-09,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7780086,-49.3450562\",-20.7780086,-49.3450562,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/r9j6_alugar_casas_sao-jose-do-rio-preto_avenida-belvedere,\"Ref.: CA95903Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Village Damha Rio Preto I..: Casa Condomnio Village Damha Rio Preto I, Casa Condominio, Dormitrios: 03, Tipo Apto.: 03, C/ A/e: 03, Cozinha: 01, Cozinha Planejada, Fogo, Lavabo, Salas : 01, Sala P/ Ambientes : 02, Piscina, Ducha Externa, Varanda, rea de Servio, Wc. de Empregada/servio, Garagem Coberta: 02, Churrasqueira\",Ref.: CA95903 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Village Damha Rio Preto I,https://thumbs4.properati.com/2/595nD1yJ6OYSKSxIr0D1iMhNilQ=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2205/060de194-4e66-4f1e-b0cc-dd05e276539f.jpg\n2568b8aa05b9d3cb3e9e8ef74c4461c34d2d57b5,2014-10-05,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8350772,-49.369111\",-20.8350772,-49.369111,3000,BRL,2371.98,768.18,,,,,,,,http://www.properati.com.br/r1q8_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95905Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Quinta das Paineiras..: Salo Comercial No Quinta das Paineiras, Salo Comercial, Terrea, Cozinha: 01, Escritrio, M2 rea Construida: 225\",Ref.: SA95905 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Quinta das Paineiras,https://thumbs4.properati.com/6/2tu7W_u475v3n8Ubll7avmjLXPI=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2130/0y7ijJHaZqFbrC0LSLFy.jpg\n0dff14f277324eb9f04d59b6c26758ccdb744e27,2014-12-05,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8126181,-49.3404534\",-20.8126181,-49.3404534,3700,BRL,2925.47,947.43,,,,,,,,http://www.properati.com.br/udzy_alugar_casas_sao-jose-do-rio-preto_avenida-miguel-damha,\"Ref.: CA95938Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha III..: Linda Casa Tipo Sobrado No Condomnio Damha Iii, Casa Condominio, Sobrado, Dormitrios: 03, Suite Master: 01, C/ Hidro, Tipo Apto.: 02, C/ A/e, Ducha Dupla, Lavatrio Duplo, Cozinha: 01, Cozinha Planejada, Despensa, Escritrio, Lavabo, Salas : 01, Sala P/ Ambientes : 02, Varanda, rea de Servio, Lavanderia, Quarto de Empregada, Wc. de Empregada/servio, Forro de Laje, Garagem Descoberta: 02, Garagem Coberta: 02, Churrasqueira\",Ref.: CA95938 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha III,https://thumbs4.properati.com/0/FGZdTzG9TG6qqioIMRxZ7WLDQWY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2452/bVU2c6naoVF7KCpphJqL.jpg\n6b3bff7cc70a79dfed41fc0ae514a7ad16a328b8,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,7000,BRL,6640.22,2150.47,,88,24.437159090909088,79.54545454545455,,,,http://www.properati.com.br/ifvc_alugar_comercial_vila-olimpia,\"\tEdifício comercial de excelente padrão, localizado em ponto nobre da Vila Olímpia, próximo da Av. dos Bandeirantes e Shopping Vila Olímpia.\t Prédio conta com 3 elevadores de alta velocidade, sistema de proteção contra incêndio, ar condicionado central, próximo a bancos. Referíªncia: CO65\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",\nc26e111d703af6f65be91df94c274bbd41585a40,2014-08-21,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.6357809,-46.5324419\",-23.6357809,-46.5324419,50000,BRL,39533.44,12803.11,,,,,,,,http://www.properati.com.br/nqbl_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95307Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Rodovia Assis ChateaubriandWcs.: 4, Escritrio, M2/ Terreno: 4.000, Detalhes: Barraco Com 4.000 M\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Rodovia Assis Chateaubriand,https://thumbs4.properati.com/0/lJ2ffPr7gtS2vlQuGTg_zDX6844=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1592/WQmB3JTS7Y2N72aUI5Ud.jpg\n2e8ffab1b55b5245e917b3597009338cb92b6586,2014-10-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7807632,-49.3985157\",-20.7807632,-49.3985157,50000,BRL,39533.44,12803.11,,,,,,,,http://www.properati.com.br/rgyw_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95804Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Waldemar VerdiM2/ Terreno: 6.000, M2 rea Construida: 3.200\",Salo,https://thumbs4.properati.com/3/X38EXdJIkvCXPQCdWURfrgMMMWQ=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2270/OTptGk75zRPMzZ1pL1on.jpg\nc522d5f57ea8e14d18fa3ba746cb581cd379330a,2014-08-20,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,100000,BRL,79066.88,25606.22,,,,,,,,http://www.properati.com.br/np98_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95721Tipo: Salo IndustrialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial IWcs.: Vrios, M2/ Terreno: 13.000, M2 rea Construida: 8.500, Detalhes: tima Localizao\",Tipo: Salo Industrial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial I,https://thumbs4.properati.com/6/jARKWDWV0rGe-HzGUtUeE0X3knw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1549/ZaCxToSHCaeCL7GxZ08g.jpg\n07383fafc72a9aa9f0286b841b782ca718485de8,2014-09-01,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178198,-49.3468294\",-20.8178198,-49.3468294,3500,BRL,2767.31,896.21,,,,,,1,,http://www.properati.com.br/o246_alugar_casas_sao-jose-do-rio-preto_avenida-nadima-damha,\"Ref.: CA95827Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha II (Res. Jardins)Dormitrios: 03, Suite: 03, C/ A/e: 03, Sacada, Sala de Visita, Copa, Cozinha, Cozinha Planejada, Despensa, Piscina, rea de Servio, Lavanderia, Wc. de Empregada/servio, Garagem Descoberta: 02, Garagem Coberta: 02, Churrasqueira, Acabamento Em Gesso, Sanca, Forro de Laje, Piso Salas: Frio;, Documentao: Em Dia.\",Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha II (Res. Jardins),https://thumbs4.properati.com/6/Bq9TbjreWNnjd2VrvJ5oieGegeQ=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1889/db5caa0b-ec86-44f4-9c0f-ab26a6816b62.jpg\n179f70a47a9ce50e161ea483ffee741a8e1c7fb2,2014-08-14,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178411,-49.387605\",-20.8178411,-49.387605,3500,BRL,2767.31,896.21,,,,,,,,http://www.properati.com.br/mpsx_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95861Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Redentora TerreaM2 rea Construida: 190, Acabamento Em Gesso, Forro de Laje, Pintura Nova, Piso Salas: Frio\",Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Redentora Terrea,https://thumbs4.properati.com/1/VQPzYdCL1X2L1IkzU17WHDuej9s=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1279/nGCLTL2QhwMJSHGtxR7y.jpg\ne48c41f6ad3dba93e0ac6038a5aa5cda39d651d0,2014-09-01,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.1423284,-51.3750141\",-22.1423284,-51.3750141,3500,BRL,2767.31,896.21,,,,,,,,http://www.properati.com.br/o2m6_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95898Tipo: Casa Comercial, Casa ResidencialCidade: So Jos do Rio Preto - SPBairro: Moyss Miguel Haddad..: Casa Residencial No Moyss Miguel Haddad, Casa Residencial, Dormitrios: 03, Suite: 03, C/ Hidro: 01, C/ A/e: 03, Sacada, Wc. Social: 01, Wcs. C/ A/e, Sala de Visita: 01, Copa: 01, Cozinha: 01, Cozinha Planejada, Despensa, Escritrio, Salas : 01, Sala P/ Ambientes : 03, Piscina, Varanda, rea de Servio, Lavanderia, Quarto de Empregada, Wc. de Empregada/servio, Ar Split, Ventilador, Interfone, Acabamento Em Gesso, Sanca, Tv Cabo, Campainha, Quintal, Grade, Murado, Forro de Laje, Piso Salas: Frio, Obs: Imvel Prximo Ao Shopping Iguatemi, M2 rea Construida: 450, Garagem Descoberta: 04, Garagem Coberta: 02, Churrasqueira, Jardim de Inverno, Jardim Interno, Iptu Valor: 199,00\",\"Ref.: CA95898 Tipo: Casa Comercial, Casa Residencial Cidade: So Jos do Rio Preto - SP Bairro: Moyss Miguel Haddad\",https://thumbs4.properati.com/5/fZwdpPACH02ObP9aj7-UFvVm0lE=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1937/a7WmbhKqWo7JqFgAHcOD.jpg\n9ea36b0e269fbf2581eef1510f0148594919b330,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,3150,BRL,2988.09,967.71,,45,21.50466666666667,70,,,,http://www.properati.com.br/ifxf_alugar_comercial_chacara-santo-antonio,\"\tNovAmérica Inside Park, o novo marco imobiliário de São Paulo. É um novo espaço, um novo bairro, uma nova opção de vida. É a expansão urbana mais consciente e mais urbana. Um mundo para morar, trabalhar e viver em plena harmonia. Uma região onde a vida social e a vida familiar se equilibram. Um lugar que concretiza o prazer de viver em uma grande metrópole. 1 torre comercial, escritórios de vila e lojas, 5 condomínios residenciais independentes e 2 parques em mais de 70.000 m² de terreno.\t\tNovAmérica Office Park é um conceito totalmente novo de local de trabalho, que valoriza eficiência e a qualidade de vida de seus ocupantes. EDSA em parceria com Neusa Nakata tiveram a missão de conceber um produto inusitado, diferenciado e particular. O resultado é um cenário privilegiado para que você e seus clientes tenham bem-estar em horário comercial.\t\tA região do NovAmérica reúne as principais empresas nacionais e multinacionais, que chegaram por aqui atraídas pela infraestrutura e pelas importantes e fáceis vias de acesso. O que confirma que a região é o novo pólo econômico de São Paulo.\t\tConheça os diferenciais do NovAmérica Office Park:\t\tÚnico na Nações Unidas a integrar um complexo formado por 1 torre comercial, escritórios de vila, lojas e 5 condomínios residenciais independentes.\tConceito consagrado de bem-estar em horário comercial.\tAcesso ao parque com mais de 10 mil m² destinado à cidade.\tPotencial de clientes vindos dos 5 condomínios residenciais.\tA oportunidade de morar e trabalhar de forma integrada e independente.\tServiços by Facilities Office.\tArquitetura e decoração inspirados nas ultimas tendências mundiais.\tProjeto paisagístico assinado por um dos mais conceituados escritórios do mundo, a EDSA. Referíªncia: CO150\",\"Comercio no(a) Chácara Santo Antônio , São Paulo - SP\",https://thumbs4.properati.com/2/2S3OsO4umzIVXFHbHKTrb7dk-x8=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/150/c0d9b8fc-3da8-4a27-ac1f-4e1684d35312.jpg\na5c14fe842664d12fe4bdfad313d5af251121d89,2014-10-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5882535,-46.6167776\",-23.5882535,-46.6167776,1400,BRL,1106.91,358.48,,,,,,,,http://www.properati.com.br/qz98_alugar_comercial_sao-jose-do-rio-preto_rua-da-imprensa,Ref.: SA95836Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Vila DinizDetalhes: Salo Comercial Com Aproximadamente 50 M e 02 Banheiros Com Acessibilidade.,Ref.: SA95836 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Vila Diniz,https://thumbs4.properati.com/9/CRgjdJjcRp1Lz-pPZChI7eL8ZIY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2075/fOuQlJsX61PBHGGwhPSh.jpg\nb64a9bd212d9e91821e5e892eeff464bbc0202cd,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,9600,BRL,9106.57,2949.21,,120,24.57675,80,,,,http://www.properati.com.br/ifwi_alugar_comercial_vila-olimpia,\"\tConjunto Comercial com 120m² de área útil, 3 vagas, 3 banheiros, 1 copa. Referíªncia: CO112\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/0/3pwYR5bzXi_fuQfUVbzkPbFkst4=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/112/93f2b4dc-86f7-4544-803f-62dfec9dc967.jpg\n59a2898d29cb6860a70355c1b2028b9ab38d89d9,2015-01-13,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5456177,-46.563645\",-23.5456177,-46.563645,1801,BRL,1459.35,472.62,,,,,,2,,http://www.properati.com.br/vyso_alugar_apartamentos_tatuape,\"APTO DE 75M/2, COM 03 DORMITRIOS, SALA AMPLA COM SACADA, COZINHA PLANEJADA, WC SOCIAL, REA DE SERVIO COM WC, REPLETOS DE ARMRIOS, 01 VAGA DE GARAGEM FIXA, EXCELENTE ESTRUTURA DE LAZER COM PISCINA, PLAYGROUD, SALO DE FESTA, CHURRASQUEIRA, SALA DE GINASTICA, EXCELENTE LOCALIZAO, A 100 METROS DO METR.\",LOCAO TATUAPE CONDOMINIO ILHAS GREGAS,https://thumbs4.properati.com/6/-xzzzVWk3lxFITvW285d3YmP00c=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/13/e7c88a20-dcac-49b2-ae42-e782447d3f07.jpg\n1925426e5575ecd98f1e63422a187081754e0f5c,2015-01-13,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5444872,-46.5657875\",-23.5444872,-46.5657875,1499,BRL,1214.64,393.37,,,,,2,1,,http://www.properati.com.br/vysp_alugar_apartamentos_tatuape_rua-serra-de-braganca,\"APTO DE 40M/2, COM 01 DORMITRIO, SALA AMPLA COM SACADA, COZINHA, WC SOCIAL, SEMI-MOBILIADO COM FOGO, SUGGAR, GELADEIRA, MICRO-ONDAS, ARMRIOS PLANEJADOS NA COZINHA, CAMA BOX, GUARDA ROUPA, VENTILADOR DE TETO, RACK DE TV, 02 BANQUETAS, 02 MESINHAS DE CANTO, EXCELENTE ESTRUTURA DE LAZER COM PISCINA, PLAYGROUD, SALO DE FESTA, CHURRASQUEIRA, SAUNA, SALA DE FITINESS, BRINQUEDOTECA, SALO DE JOGOS, SALA DE LEITURA. EXCELENTE LOCALIZAO, PRXIMO DO SUPERMERCADO PO DE ACAR, FCIL ACESSO PARA O METR.\",NEW CONCEPT 22 A,https://thumbs4.properati.com/5/SXF7T5Zfxal6lcQifCYOufBKjYA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/14/0522b83e-c37e-4736-ac81-ca8540d2d806.jpg\nfda45d95b6fe8daf9215dcc80ad057531879eda4,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,4575,BRL,4339.84,1405.48,,120,11.712333333333333,38.125,,,,http://www.properati.com.br/ifxz_alugar_comercial_vila-olimpia,\"\tConjunto Comercial na Vila Olimpia, com 61m², podendo aumentar a área para 117m². Referíªncia: CO179\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/5/cZY_8L_8bfKOV9ay-F-6DsnRZ-c=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/179/0df8ffa9-49a9-49c0-9968-37c13719d904.jpg\n7eb9fb8ad21ff1da490b75fd04643939fd72edf1,2015-01-13,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5508578,-46.5679013\",-23.5508578,-46.5679013,1550,BRL,1255.99,406.76,,,,,11,1,,http://www.properati.com.br/vysn_alugar_apartamentos_tatuape,\"APTO DE 57M/2, COM 02 DORMITRIOS, SALA AMPLA COM SACADA, WC SOCIAL, COZINHA PLANEJADA, AMPLA REA DE SERVIO COM WC, 01 VAGA DE GARAGEM, SALO DE FESTA, CHURRASQUEIRA, PLAYGROUD, QUADRA DE FUTEBOL, EXCELENTE LOCALIZAO, PRXIMO AO SHOPP/METR BOULEVAR TATUAP.\",Apartamento,https://thumbs4.properati.com/4/mwisK4iz0H0viKmBVOGr8B40c1o=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/12/612ab301-0ac4-4c4c-9691-7278200c2b62.jpg\n59a2934bc5a4c3530613fae471a210ae63498d80,2014-09-02,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.826094,-49.376396\",-20.826094,-49.376396,1200,BRL,960.76,311.15,,,,,,1,,http://www.properati.com.br/ob16_alugar_apartamentos_sao-jose-do-rio-preto,\"Apartamento mobiliado, pronto para morar e com excelente localizao, na zona sul.2 dormitrios sendo 1 tipo apto todos com armrio, ventilador de teto e ar-condicionadosala com sacada para 2 ambientes com sofa e estantecozinha planejada com mesa com 4 cadeiras e fogo2 banheiros com box blindex, gabinete e chuveirorea de servio com armrio, varal e mquina de lavar1 vaga de garagem pilotisCondomnio: Porto eletrnico, portaria 24h, elevador, churrasqueira e piscina.\",Apartamento com 2 dormitrios em Rio Preto,https://thumbs4.properati.com/5/VXcYDJRN6VtNxvgVw-mxJOlL_sA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/premier/photos/111/91c65753-86b6-40fd-8673-7f8f107a8459.jpg\nd08581768058f5286ca4f209222d59a3f10af84d,2015-01-13,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-22.8877586,-47.081265\",-22.8877586,-47.081265,2401,BRL,1945.56,630.08,,,,,19,2,,http://www.properati.com.br/vysm_alugar_apartamentos_tatuape,\"APTO DE 93M/2, COM 03 DORMITRIOS, SENDO UMA SUTE, SALA AMPLA COM SACADA E DOIS AMBIENTES, WC SOCIAL, COZINHA, AMPLA REA DE SERVIO, REPLETO DE ARMRIOS, 02 VAGAS DE GARAGEM, LAZER COMPLETO COM PISCINA, PLAYGROUD, SALO DE FESTA, SALO DE JOGOS, SAUNA, CHURRASQUEIRA, EXCELENTE LOCALIZAO, PRXIMO DA PRAA SILVIO ROMERO.\",APTO LOCAO TATUAP,https://thumbs4.properati.com/3/d45ivnQIryA-xDQvwZrOc1pBPlE=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/11/85e5aee8-90fa-4295-bcb5-dce1ed7e789c.jpg\n7739326a3915df1654866f5c27d42e40f0038365,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2200,BRL,2086.92,675.86,,102,6.626078431372549,21.568627450980394,,,,http://www.properati.com.br/ifwp_alugar_comercial_vila-olimpia,\"\tConjunto Comercial com 46m², 2 banheiros, 3 salas, 1 vaga. Referíªncia: CO122\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/6/QhZrO4ITAg_PJagrwPKR0ywe4GI=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/122/80e20053-aaba-4f3b-a8f7-9dc2cdcb44a7.jpg\n4d949c2fcb78e76cf0cb2376ae3a3acc3b27994a,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2200,BRL,2086.92,675.86,,46,14.692608695652174,47.82608695652174,,,,http://www.properati.com.br/ifut_alugar_comercial_vila-olimpia,\"Conjunto Comercial na Rua Helena, com 46m², 1 vaga. Referíªncia: CO44\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/7/rah-1WLiSRF_nrLl-U2mEY27Gus=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/44/3d3a55f5-dd0c-4969-bb57-19f9a554c1f2.jpg\nc98dc531eb6cb68bcec0e60d7dc042bd663caa4d,2014-08-02,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.815191,-49.389435\",-20.815191,-49.389435,5000,BRL,4003.24,1296.47,,,,,,,,http://www.properati.com.br/ludd_alugar_casas_sao-jose-do-rio-preto,\"Imvel com excelente localizao, com recepo, 1 sala com banheiro privativo, 3 salas amplas + 1 interna, todos os ambientes com ar condicionado, cozinha, despensa, garagem interna para vrios veculos. IMVEL SER ENTREGUE REFORMADO E COM AS ACESSIBILIDADES EXIGIDAS!\",Casa Comercial,https://thumbs4.properati.com/8/3TeTIOIgyjtraKZz08buZLIU8W8=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/premier/photos/80/6a0fcb3e-3e78-434f-9624-a5621798b2b2.jpg\ne85c67a2bc55f4834be8d4cf1fd96340ef4ce129,2014-08-04,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8379877,-49.385659\",-20.8379877,-49.385659,800,BRL,632.5,204.84,,,,,,1,,http://www.properati.com.br/lxpc_alugar_casas_higienopolis,\"Ref.: AP95874 Tipo: ApartamentoCidade: So Jos do Rio Preto - SPBairro: Higienpolis..: Apartamento Novo No Jardim Higienopolis, Apartamento, rea Util M2: 50, Dormitrios: 01, C/ A/e, Sacada, Wc. Social: 01, Cozinha C/ A/e, Lavanderia, Elevador, Piso Dormitrios: Porcelanato, Piso Salas: Porcelanato, Obs: Prdio Novo.apto Nunca Habitado., rea de Lazer, Garagem Coberta: 01, Churrasqueira, Espao Gourmet\",Tipo: Apartamento Cidade: So Jos do Rio Preto - SP Bairro: Higienpolis,https://thumbs4.properati.com/6/8tmxgf5PTdg11vI4rVdPwXy39gw=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/806/2077f853-c360-4cd7-b5c8-a33f76415c40.jpg\n33b22b30a043e5a868df61862bb97698b453df89,2014-12-05,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7910821,-49.3781115\",-20.7910821,-49.3781115,800,BRL,632.5,204.84,,,,,,1,,http://www.properati.com.br/ue05_alugar_apartamentos_sao-jose-do-rio-preto_rua-pedro-mulati,\"Ref.: AP95816Tipo: ApartamentoCidade: So Jos do Rio Preto - SPBairro: Jardim ConceioDormitrios: 02, C/ A/e, Wc. Social, Box Wcs., Sala de Estar, Cozinha, Piscina, rea de Servio, Garagem Coberta: 01, rea Util M2: 60, Condominio C/ rea de Lazer, Piscina Adulto, Playground, Valor Condominio R$-: 190,00\",Ref.: AP95816 Tipo: Apartamento Cidade: So Jos do Rio Preto - SP Bairro: Jardim Conceio,https://thumbs4.properati.com/2/4RA-EyT8LswOeQ6lcBbkzLlKjII=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2459/jWyxMNGlETMMxgj2uW9o.jpg\ne2235d97414fcc1a31865b1abe71c1cf85ae01fa,2014-11-17,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7780086,-49.3450562\",-20.7780086,-49.3450562,2700,BRL,2134.78,691.36,,,,,,,,http://www.properati.com.br/tdi5_alugar_casas_sao-jose-do-rio-preto_avenida-belvedere,\"Ref.: CA95926Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Village Damha Rio Preto I..: Casa Condomnio Village Damha Rio Preto, Casa Condominio, Terrea, Dormitrios: 3, Suite: 1, Tipo Apto.: 2, C/ A/e, Lavatrio Duplo: 1, Cozinha C/ A/e: 1, Cozinha Planejada, Escritrio, Lavabo, Salas : 1, Sala P/ Ambientes : 2, Varanda, rea de Servio, Wc. de Empregada/servio, Obs: Espao Para Colocar Piscina, M2 rea Construida: 180, Garagem Descoberta: 2, Garagem Coberta: 2, Churrasqueira, Detalhes: Com Armrios .\",Ref.: CA95926 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Village Damha Rio Preto I ,https://thumbs4.properati.com/3/ZZ3MVCiFvwDaZxqgfl5oQJ1Vp6c=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2390/5d2ae653-6549-4772-b486-cd9360af6ebd.jpg\n1334a26f10895f5a21b6db0adf96e2ff1dc83799,2014-09-16,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7780086,-49.3450562\",-20.7780086,-49.3450562,1250,BRL,988.31,320.07,,,,,,,,http://www.properati.com.br/q4jz_alugar_casas_sao-jose-do-rio-preto_avenida-belvedere,\"Ref.: CA95901Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Maria Stella..: Casa Condomnio Maria Stella, Casa Condominio, Terrea, Dormitrios: 3, Tipo Apto.: 1, Wc. Social: 1, Cozinha: 1, Cozinha Planejada, Salas : 01, Sala P/ Ambientes : 02, Varanda, rea de Servio, Lavanderia, Garagem Coberta: 02, Salo de Festa, Playground, Piscina Adulto, Quadras Poliesportivas: 1, Campo de Futebol: 1, Valor Condominio R$-: 260,00\",Ref.: CA95901 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Maria Stella,https://thumbs4.properati.com/3/nKZAghWN-90yQywuniXpPPLO9sk=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2011/e55aad60-d657-4c71-b732-96032067b76f.jpg\n40c34e74b932ff6e10ff49774a8f7fb6e982c1c2,2014-11-30,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8081017,-49.3505771\",-20.8081017,-49.3505771,4500,BRL,3558.01,1152.28,,,,,,,,http://www.properati.com.br/tz24_alugar_comercial_sao-jose-do-rio-preto_rua-valdomiro-nadotti,\"Ref.: SA95928Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Tres Maria..: Salo Comercial No Bairro Trs Maria, Salo Comercial, Detalhes: 160 Metros Mais 40 Metros de Mesanino.\",Ref.: SA95928 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Tres Maria,https://thumbs4.properati.com/3/Yg9eVpfZKwd2s12w6w1yQ4YYVhY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2402/MTVaVFERWIxvf0IcXQaQ.jpg\n716428a7c9f46f98fd78e36a72109b0c0ec43753,2014-11-30,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8081017,-49.3505771\",-20.8081017,-49.3505771,4500,BRL,3558.01,1152.28,,,,,,,,http://www.properati.com.br/tz25_alugar_comercial_sao-jose-do-rio-preto_rua-valdomiro-nadotti,\"Ref.: SA95929Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Tres Maria..: Salo Comercial No Bairro Trs Maria, Salo Comercial, M2/ Terreno: 200, Detalhes: 140 Metros Mais 40 Metros de Mesanino.\",Ref.: SA95929 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Tres Maria,https://thumbs4.properati.com/9/a_rVYKLimoBl774T612e8o5Icfo=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2403/fhkihJbLNgNCmHo7eBRG.jpg\n2102860e4c561ee2afec1c15fb71f7c6cfafc28d,2014-08-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,9000,BRL,7116.02,2304.56,,,,,,,,http://www.properati.com.br/muuy_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95893Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Ch. Jockey Clube..: Salo Comercial Na Chcara Jockey Clube, Salo Comercial, M2/ Terreno: 5.000, M2 rea Construida: 1.200, Detalhes: Galpo Com Piso Cimento Polido, Estrutura Metlica, Escritrio, Cozinha, 02 Wc, Entrada Pela Frente e Lateral Para Caminho.p Direito Alto Com 6,/2 M.\",Ref.: SA95893 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Ch. Jockey Clube,https://thumbs4.properati.com/3/H-b0Ol05_NX0A5soGXBvD_dA3wk=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1482/gCB4KEDxOoIlG9HlGbCW.jpg\ndb16c4572d4f19875153b041d1dcb6b82115e6a7,2014-11-03,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8174527,-49.4306981\",-20.8174527,-49.4306981,18000,BRL,14232.04,4609.12,,,,,,,,http://www.properati.com.br/sjyy_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95923Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Parque Industrial..: Salo Comercial No Parque Industrial, Salo Comercial, M2/ Terreno: 3.000, M2 rea Construida: 1.780, Detalhes: 1.000m de Patio.escritrio Com Elevado Vrias Salas, Cozinha, Wc, Vestirio Mas/fem;04 Salas de Descanso Para Motorista Com Vestirio, 02 Chuveiros e 04 Wc;cozinha Externa Com Quiosque + 02 Wc Mas/fem;parte Operacional Com P Direito de 7 Metros, e A Lateral Toda Com Embarque e Desembarque;ptio Com Espao Para Carretas, Caminhes e Carros;portaria Na Entrada do Ptio.\",Ref.: SA95923 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Parque Industrial,https://thumbs4.properati.com/0/wtHJ8Xcu4pqyV7YSP06g_N1yrfg=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2356/rO0uXvqSM9ilLshNDz0f.jpg\nc8c81389391a237255bade4a0608212fa62e13b1,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,4200,BRL,3984.12,1290.28,,107,12.058691588785047,39.25233644859813,,,,http://www.properati.com.br/ifxy_alugar_comercial_vila-olimpia,\"\tConjunto Comercial na Vila Olimpia, com 56m² de área útil, excelente localização.  Referíªncia: CO178\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/7/YjqSlFDSnOcayBrp2UtD_X2xaJM=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/178/fa4df6b6-bb36-40a4-a46b-7b1438c77d03.jpg\ne1f129a3f1296a7e7a3b60d5b96d61c49159900c,2014-11-19,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.9295411,-46.3418105\",-23.9295411,-46.3418105,1600,BRL,1628.47,527.39,,32,16.4809375,50,,1,,http://www.properati.com.br/tkvf_alugar_apartamentos_sao-paulo,\"A 3 Milenio oferece este(a) Apartamento em Sao Vicente, no bairro Morro dos Barbosas - 1 dormitórios. Gostou desse(a) Apartamento em Sao Vicente? Então veja mais informações! Acesse o nosso site clicando no botão Contate o anunciante ou ligue - ou ainda entre em contato conosco pelo email vendas@ Obrigado!\",\"Apartamento em Sao Vicente, no bairro Morro dos Barbosas - 1 dormitórios\",https://thumbs4.properati.com/5/FvlCdG4KWDtvmzsszDUttj3AuAY=/trim/198x0/smart/filters:strip_icc()/www.imobiliariasdesantos.com.br/fotos/F00/72/807200-DSC03573.JPG\na8821d1c24cccd45d37ef69cf2b29ef3be744fad,2014-11-17,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8456698,-49.4030718\",-20.8456698,-49.4030718,3200,BRL,2530.11,819.39,,,,,,1,,http://www.properati.com.br/tdi2_alugar_apartamentos_sao-jose-do-rio-preto_avenida-anisio-haddad,\"Ref.: AP95925Tipo: ApartamentoCidade: So Jos do Rio Preto - SPBairro: Cond. Green Fields..: Apartamento No Condomnio Green Fields, Apartamento, rea Util M2: 152, Dormitrios: 03, Suite Master: 01, Closet , Tipo Apto.: 02, C/ A/e: 02, Wc. Social: 02, Sala de Visita: 01, Cozinha Planejada, Garagem Coberta: 02\",Ref.: AP95925 Tipo: Apartamento Cidade: So Jos do Rio Preto - SP Bairro: Cond. Green Fields,https://thumbs4.properati.com/4/oRIx16Fpw4LPkRsB59wlBS_mxVo=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2387/ae2a6d87-5760-44ff-a47f-d237a5321c3c.jpg\n08918f4a93c54f4505b783a8e31ef11842c55e56,2014-10-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5882535,-46.6167776\",-23.5882535,-46.6167776,80000,BRL,63253.52,20484.98,,,,,,,,http://www.properati.com.br/qz9a_alugar_comercial_sao-jose-do-rio-preto_rua-da-imprensa,\"Ref.: SA95835Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Vila DinizM2/ Terreno: 12.000, Detalhes: - Salo Com 5.000 M e Bastante rea Para Estacionamento Aberto.Ideal Para Atacadista, Hipermercado e Distribuidora, Entre Outros.\",Ref.: SA95835 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Vila Diniz,https://thumbs4.properati.com/1/PkKXX6cNiwu9krqBtdWDinLG89A=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2077/vSsX8s9IzXdbNCXPId0W.jpg\n1ce358d0b5c35f146d70dff4089a8b3d287cb7b9,2014-10-08,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8085627,-49.3452813\",-20.8085627,-49.3452813,5500,BRL,4348.67,1408.34,,,,,,,,http://www.properati.com.br/r6de_alugar_comercial_sao-jose-do-rio-preto_avenida-sebastiao-tavares-da-silva,\"Ref.: SA95809Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Vista AlegreM2/ Terreno: 360, M2 rea Construida: 360, Detalhes: Salo Com Vrias Divisrias.\",Salo,https://thumbs4.properati.com/8/dhtnMWfIFZ8mMCFVDpxpvjoF0LA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2184/l63oynNjEecn8u1Dzf3W.jpg\nb6ed51b61bb57831cc00d904741b4f544cafc28f,2014-08-04,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178198,-49.3468294\",-20.8178198,-49.3468294,5500,BRL,4348.67,1408.34,,,,,,1,,http://www.properati.com.br/lxoz_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95886Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha II(Res. Jardins)..: Casa Condomnio Damha II, Casa Condominio, Sobrado, Dormitrios: 03, Suite: 01, C/ Hidro, Tipo Apto.: 02, C/ A/e: 03, Roupeiro, Home, Cozinha: 01, Cozinha Planejada, Despensa, Escritrio, Lavabo, Salas : 01, Sala P/ Ambientes : 03, Piscina, Varanda, rea de Servio, Quarto de Empregada, Wc. de Empregada/servio, Aquecedor Solar, M2 rea Construida: 350, rea de Lazer, Garagem Descoberta: 03, Garagem Coberta: 03, Churrasqueira\", Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha II,https://thumbs4.properati.com/1/gEw1FIkmFLn2_zGBjqnuUzcifYU=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/793/e9d2d588-8d92-4070-b7d6-5b55e4484038.jpg\n1273c64fb8b9402dfad98d3984dec2998d8bdb68,2014-08-15,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8126181,-49.3404534\",-20.8126181,-49.3404534,5500,BRL,4348.67,1408.34,,,,,,1,,http://www.properati.com.br/mqtl_alugar_casas_sao-jose-do-rio-preto_avenida-miguel-damha,\"Ref.: CA95731Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha IVDormitrios: 03, Suite: 01, Closet : 01, C/ Hidro, Tipo Apto.: 02, C/ A/e: 02, Wcs.: 05, Wcs. C/ A/e: 04, Box Wcs.: 04, Copa: 01, Cozinha: 01, Cozinha C/ A/e, Despensa, Escritrio, Lavabo, Salas : 01, Sala P/ Ambientes : 02, Piscina, Varanda, rea de Servio, Wc. de Empregada/servio, M2/ Terreno: 420, M2 rea Construida: 380, rea de Lazer, Garagem Descoberta: 02, Garagem Coberta: 02, Churrasqueira, Piso Dormitrios: Porcelanatoq, Piso Salas: Porcelanato, Obs: Imvel Muito Bem Conservado, timo Acabamento., Valor Condominio R$-: 300,00, Detalhes: Toda Reformada.\",Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha IV,https://thumbs4.properati.com/6/H5vuIYMzk8pMuWJ-OjcdH4GItq8=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1367/9717e95d-fe6f-4cbc-bb5d-21c2fe92d294.jpg\n6aa6bf8a204f123441efb9f97699ee24cf8f9932,2014-08-15,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8178198,-49.3468294\",-20.8178198,-49.3468294,5500,BRL,4348.67,1408.34,,,,,,1,,http://www.properati.com.br/mqtn_alugar_casas_sao-jose-do-rio-preto_avenida-nadima-damha,\"Ref.: CA95797Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Damha ISobrado, Dormitrios: 03, Suite: 01, Tipo Apto.: 02, Wcs.: 01, Cozinha Planejada, Escritrio, Lavabo, Salas : 01, Sala P/ Ambientes : 03, Piscina, rea de Servio, Wc. de Empregada/servio, rea de Lazer, Garagem Coberta: 03, Churrasqueira, Ar Condicionado, Ventilador, Interfone, Piso Dormitrios: Taco, Piso Salas: Frio\",Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Damha I,https://thumbs4.properati.com/0/NAjAu7cPHQwzwT2j3k1G4X7-A3U=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1369/7b6ca8dd-648a-4e18-b9f5-272073bfbf69.jpg\n8a977c0c4b9ef700024cb2ee3e59de53bcf599c8,2014-07-16,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2000,BRL,1897.2,614.42,,70,8.77742857142857,28.571428571428573,,2,,http://www.properati.com.br/ifzl_alugar_apartamentos_vila-olimpia,\"\tApartamento na Vila Olimpia, sala para 2 ambientes, 2 dormitórios com armários embutidos, cozinha, lavanderia, quarto na área de serviço, 2 banheiros, 1 vaga. Referíªncia: AP237\",\"Apartamento no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/9/waPGo7zWMk2XTzN-br6B0h4Nrgo=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/237/75daca8f-ee37-4813-97af-b4fa00920d4f.jpg\nb48b18eca01fc2732d6afe024904eb1e900cc52a,2014-07-16,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2000,BRL,1897.2,614.42,,54,11.378148148148147,37.03703703703704,,2,,http://www.properati.com.br/ifzn_alugar_apartamentos_saude,\"\tÓtimo apartamento proximo ao Metrê Praça da Arvore, sala para 2 ambientes, 2 dormitórios com armários embutidos, cozinha com armários, banheiro social e de serviço, 1 vaga na garagem, quadra, piscina e play.\tCom pintura nova, pronto para morar!! Referíªncia: AP239\",\"Apartamento no(a) Saúde , São Paulo - SP\",https://thumbs4.properati.com/0/Zof1-heqqvAgC3xdyYRYiQHIX2I=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/239/450071da-5288-4f26-bbb4-66bcacaef572.jpg\n636cbd951ef34773973cfdef37746b911d07a24c,2014-07-16,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,4000,BRL,3794.41,1228.84,,200,6.1442,20,,4,,http://www.properati.com.br/ifxa_alugar_casas_saude,\"\tAluga sobrado em zona mista, residencial e ou serviços, a uma quadra da Av. do Cursino e a 1000m, do Shopping Plaza Sul, 4 dormitórios, sendo uma suite, ampla sala, mais sala de jantar, cozinha, edicula com 3 quartos, mais lavanderia, garagem para 3 veiculos, casa de esquina, com portões automaticos.  Referíªncia: CA145\",\"Casa no(a) Saúde , São Paulo - SP\",https://thumbs4.properati.com/3/0sie3N08lMquoZ655zNy1CeDUbU=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/145/9fef5234-2fa4-4974-b24c-2b3cf6c4c542.jpg\n0812a419c00dcbaaaa926ad4e8de47da7959f9d9,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,5700,BRL,5407.01,1751.09,,127,13.788110236220472,44.881889763779526,,,,http://www.properati.com.br/ifzp_alugar_comercial_saude,\"\tConjunto Comercial proximo do Metrô São Judas, com 109m² de área útil, dividido em 5 salas, com possibilidade de vão livre, 3 banheiro, 1 copa, 3 vagas, prédio 24 horas.  Referíªncia: CO241\",\"Comercio no(a) Saúde , São Paulo - SP\",https://thumbs4.properati.com/1/0kl3BRCM4riJX3LhCObIu4wuBxQ=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/241/25ac4ee5-f867-4808-b226-837aefd7ba2c.jpg\n570fece17687d05345022d1567c79dd80bd1dcb0,2014-12-19,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8312527,-49.360102\",-20.8312527,-49.360102,15000,BRL,11860.02,3840.93,,,,,,,,http://www.properati.com.br/viin_alugar_comercial_sao-jose-do-rio-preto_avenida-marginal-lineu-alcantara-gil,\"Ref.: SA95950Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Campo Verde..: Salo Comercial No Distrito Industrial Campo Verde, Salo Comercial, M2/ Terreno: 5.000, M2 rea Construida: 3.000, Detalhes: tima Localizao Com Asfalto Para Rua, Estacionamento Para 60 Carros.barraco 5.000m2c/ Escritrio Isolado Ao Lado do Galpo C/ 6 Salas Grandes Von Livre Cond. Port.24h.\",Ref.: SA95950 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial Campo Verde,https://thumbs4.properati.com/6/7lTVZ92lzm8ap5amDGM_eb1EHOU=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2528/yS1CaXFW8XYwJdPuhgGn.jpg\n2ee8e3818d61fdc4e01a5d89be181974d74e4380,2014-08-15,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8424141,-49.3549387\",-20.8424141,-49.3549387,15000,BRL,11860.02,3840.93,,,,,,,,http://www.properati.com.br/mqtc_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95109Tipo: Salo Comercial, Salo IndustrialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Tancredo Neves .Terrea M2/ Terreno: 2.100, M2 rea Construida: 936, Detalhes: Estacionamento Na Frente,entrada Lateral Para Carga e Descarga,prox. do Carrefour\",\"Tipo: Salo Comercial, Salo Industrial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial Tancredo Neves .\",https://thumbs4.properati.com/9/KVlFV9eR-3HhzpzA5EtDKJICEgY=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1358/k6zhNAyNgH9P1excuuTM.jpg\n0f0577616ba85404705aaead7f35ff6e8b4a91da,2014-08-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5733308,-46.544242\",-23.5733308,-46.544242,15000,BRL,11860.02,3840.93,,,,,,,,http://www.properati.com.br/mt62_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: CO95825 Tipo: Predio Comercial, Salo Comercial, Salo Industrial Cidade: So Jos do Rio Preto - SP Bairro: Pq. IndustrialWc. Social: 02, Salas : 06, M2/ Terreno: 1.113, Documentao: Em Dia., Detalhes: Banheiro Feminino e Masculino, 06 Salas Amplas, Sendo de 420 M a Maior.o Salo Possui Ambientes Para Reunies, Sala de Refeitrio de 93 M e Armrio Embutido, Alarme, Campainha, Interfone, Cerca Eltrica e Estacionamento de 258 M Com 25 Vagas Cobertas.\",\"Tipo: Predio Comercial, Salo Comercial, Salo Industrial Cidade: So Jos do Rio Preto - SP Bairro: Pq. Industrial\",https://thumbs4.properati.com/5/ug_ZifuqqZGKLp9_hQAm2sSvkgE=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/1436/Q14vgp5W0wZIU71vOSui.jpg\n8d6a13266a268bd8dd46f19df78ef71e3509ee5a,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7700326,-49.3956468\",-20.7700326,-49.3956468,6500,BRL,5139.33,1664.4,,,,,,,,http://www.properati.com.br/ri3n_alugar_comercial_sao-jose-do-rio-preto_avenida-ada-marzochi-polachini,\"Ref.: SA95917Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Mini Dist. Industrial Adail Vetorasso..: Salo Comercial No Mini Destrito Industrial Adail Vetorasso, Salo Comercial, Wcs.: 02, Cozinha: 01, Escritrio, M2/ Terreno: 1.050, M2 rea Construida: 680, Detalhes: Barraco Novo, Com Pilar No Centro;porta Na Frente e Lateral;cobertura Metlica;p Direito 6 Metros.\",Ref.: SA95917 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Mini Dist. Industrial Adail Vetorasso,https://thumbs4.properati.com/2/3gLCbr6emuqSINA3fAYUW1zaIUM=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2283/grmYrDDM8HnJtcT4RzOD.jpg\ndac06563fc851c6596496ceaff68cbf177bb6dde,2014-10-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7863702,-49.3690389\",-20.7863702,-49.3690389,6500,BRL,5139.33,1664.4,,,,,,,,http://www.properati.com.br/qz9p_alugar_comercial_sao-jose-do-rio-preto_avenida-solon-da-silva-varginha,\"Ref.: SA95834Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Vila NovaesWc. Social: 03, Mesanino, M2/ Terreno: 530, M2 rea Construida: 480, Detalhes: Salo Comercial Com P Direito de 6m e Mezanino de 55 M.possui Aparelhagem de Ar Condicionado, Som Ambiente, Cmeras de Segurana e Estacionamento Para 18 Carros.local Ideal Para Lojas, Mercados, Igreja, Depsito, Entre Outros.\",Ref.: SA95834 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Vila Novaes,https://thumbs4.properati.com/8/qD9kCsVbiYyB0WGFw5WesI2Ny6s=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2092/d62f4d68-2ea5-4f83-bde9-fdc53bb504f2.jpg\n1ccba6f23edef69215ca9cfe74214a5276597714,2014-10-18,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,6500,BRL,5139.33,1664.4,,,,,,,,http://www.properati.com.br/ri3q_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: SA95919Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Dist. Industrial Centenrio da Emancipao..: Salo Comercial No Distrito Industrial Centenrio da Emancipao., Salo Comercial, Cozinha: 02, Mesanino, Escritrio, M2 rea Construida: 1.015, Detalhes: Escritrio, Cozinha, 02 Wc, Mezanino, 2 Salas, Entrada Lateral.salo 700 Metrosdiviso 315 Metros.\",Ref.: SA95919 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Dist. Industrial Centenrio da Emancipao,https://thumbs4.properati.com/0/FlEAiRaerT0pRZEeW7AhhCuJjJc=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2286/v22UnUeHl8YbhJaP8IaU.jpg\na9d04cd869b56cac7bcb3a54a27147996a973a3d,2015-01-20,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5418835,-46.5583192\",-23.5418835,-46.5583192,1900,BRL,1539.57,498.6,,,,,16,1,,http://www.properati.com.br/w730_alugar_apartamentos_tatuape,\"APTO DE 58M/2, COM 02 DORMITRIOS, SENDO UMA SUTE, SALA AMPLA COM SACADA, COZINHA, WC SOCIAL, REA DE SERVIO, COM ALGUMAS MOBILIAS, TUDO DE PRIMEIRA, 02 VAGAS DE GARAGEM, LAZER COMPLETE, EXCELENTE LOCALIZAO, A UMA QUADRA DO METR.\",MOBILIADO EXCLUSIVE,https://thumbs4.properati.com/2/KuQXATs4BGrSaZvfCzOSMN2pqWs=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/19/30ad0c15-c9ef-48f7-a06e-2a0201bb7433.jpg\n5de8d0e3c8872b0af03bdc517a4a12021c48da59,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,24700,BRL,23430.5,7588.09,,289,26.25636678200692,85.46712802768167,,,,http://www.properati.com.br/ifvd_alugar_comercial_vila-olimpia,\"EDIFí�CIO COMERCIAL DE ALTí�SSIMO PADRí�O,  LOCALIZADO NO PONTO MAIS NOBRE DA REGIí�O, EM FRENTE AOSHOPPING VILA OLí�MPIA, ENTRE AV. DOS BANDEIRANTES E JUSCELINO KUBITSCHEK. OPRí�DIO CONTA COM 6 ELEVADORES DE ALTA VELOCIDADE, SENDO 1 PANORí�MICO, ARCONDICIONADO CENTRAL, FACHADA EM CORTINA DE VIDRO. EDIFí�CIO INTELIGENTE COMSOFISTICADO SISTEMA DE CONTROLE DE INCí�NDIO, CONSUMO DE ENERGIA. Referíªncia: CO66\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",\n705fdcf676aca2c9f51fcc0a332fcff39ee1706c,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,19500,BRL,18497.74,5990.59,,260,23.04073076923077,75,,,,http://www.properati.com.br/ifyk_alugar_comercial_vila-olimpia,\"\tConjunto Comercial com 260m² de área útil, 6 vagas, Ar condicionado Central. Referíªncia: CO200\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/0/-wzyHwNVYSdVIvzDSj7dz-RtyfY=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/200/f2ef8797-b051-4b62-b4fe-e8fdb7523a26.jpg\n179ce00d44c6fafbac93e139f052ab4ce304100d,2014-08-04,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,4200,BRL,3320.8,1075.46,,,,,,,,http://www.properati.com.br/lxpa_alugar_casas_sao-jose-do-rio-preto,\"Ref.: CA95877 Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SPBairro: Cond. Village La MontangneDormitrios: 3, Suite: 1, C/ Hidro: 1, Tipo Apto.: 2, C/ A/e: 3, Cozinha Planejada, Escritrio, Lavabo, Salas : 1, Sala P/ Ambientes : 3, Piscina, rea de Servio, M2/ Terreno: 360, M2 rea Construida: 250, Garagem Descoberta: 2, Garagem Coberta: 2\",Tipo: Casa Condominio Cidade: So Jos do Rio Preto - SP Bairro: Cond. Village La Montangne,https://thumbs4.properati.com/8/XTvYImGZcecKNC8xwzjnrrqWibg=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/804/ce485ed8-97e3-4af6-bc5e-3d347e06b31d.jpg\n868d3ff8ea898557e9819638705d2f823431d000,2015-01-13,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.521407,-46.5769202\",-23.521407,-46.5769202,1500,BRL,1215.45,393.63,,,,,13,1,,http://www.properati.com.br/vysr_alugar_apartamentos_tatuape,\"APTO DE 60M/2, COM 02 DORMITRIO, SALA AMPLA COM SACADA, WC SOCIAL, COZINHA, REA DE SERVIO, 01 VAGA DE GARAGEM, EXCELENTE ESTRUTURA DE LAZER COM PISCINA, PLAYGROUD, SALO DE FESTA, CHURRASQUEIRA, EXCELENTE LOCALIZAO, FCIL ACESSO AO SHOPPING METR/TATUAP.\",131 VITORIA,https://thumbs4.properati.com/0/yyioUci-ExAIuKlfIudXKNKaMAs=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/toledo/photos/16/ff90ae98-dbde-47dc-9f04-32cdb2d2d0d3.jpg\nb9de26b4b13ad0f58469677b72b5171c2b04ae1f,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,13500,BRL,12806.12,4147.33,,183,22.663005464480875,73.77049180327869,,,,http://www.properati.com.br/ifv7_alugar_comercial_vila-olimpia,CONJUNTO COMERCIAL - RECEPCAO - HD - 4 SALAS - 2 WC - COPA  Referíªncia: CO60,\"Comercio no(a) Vila Olímpia , São Paulo - SP\",\n416be9056b69e83819532b65a04c6d1f7135f1ca,2014-12-11,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2400,BRL,1897.57,614.54,,,,,,,,http://www.properati.com.br/uict_alugar_comercial_jardim-maracana,\"Ref.: SA95945Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Jardim Maracan..: Salo Comercial No Jardim Maracan, Salo Comercial, Wc. Social: 02, Cozinha: 01, Forro de Laje, Piso Salas: Frio, M2/ Terreno: 225, M2 rea Construida: 130\",Ref.: SA95945 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Jardim Maracan,https://thumbs4.properati.com/5/3964ltfTag6YXQlyxEM19akYy1s=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2500/f25a8d90-6759-426c-a4ed-d8aefc6b19a4.jpg\nc1aff8edd38c3254c5775927802a8b8fae45a1d8,2014-08-05,rent,house,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,2400,BRL,1897.57,614.54,,,,,,1,,http://www.properati.com.br/mel5_alugar_casas_bosque-da-saude,\"Ref.: CA95883Tipo: Casa ResidencialCidade: So Jos do Rio Preto - SPBairro: Bosque Da Saude..: Casa Residencial No Bosque da Saude, Casa Residencial, Dormitrios: 03, Tipo Apto.: 01, C/ A/e: 03, Wc. Social: 01, Copa: 01, Cozinha: 01, Despensa, Salas : 02, Varanda, Garagem Descoberta: 02, Garagem Coberta: 02\",Tipo: Casa Residencial Cidade: So Jos do Rio Preto - SP Bairro: Bosque Da Saude,https://thumbs4.properati.com/0/aAdm951DKitDcqkiJ8p68O6xdNA=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/848/083f9a71-b22a-4897-bf14-72fbe585dea5.jpg\ne4d569301fb8a21047d67f193929ecf360e9405e,2015-01-11,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,750,BRL,600.6,194.51,,,,,,1,,http://www.properati.com.br/vxnv_alugar_apartamentos_jardim-satelite,\"Excelente apartamento em frente ao condomnio Quinta das Flores totalmente reformado com 2 dormitrios, sala, cozinha planejada, rea de servio, lavabo com gabinete, wc com box blindex e 1 vaga de garagem.Ideal para casal sem filhos e sem animais\",Excelente apartamento no Jardim Satlite,https://thumbs4.properati.com/1/RhUtbG6YnK8FCa_yOduiEf3yyj0=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/tyrreno/photos/1074/71219c81-463f-4710-a46d-5e7e2fee9362.jpg\n6b59e27767f7570db49b2175077b1c059fa20b88,2014-10-01,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-23.5715766,-46.6697673\",-23.5715766,-46.6697673,3100,BRL,2451.06,793.79,,,,,,,,http://www.properati.com.br/qz9r_alugar_comercial_sao-jose-do-rio-preto_avenida-brasil,\"Ref.: SA95828Tipo: Salo ComercialCidade: So Jos do Rio Preto - SPBairro: Jardim MugnaniWc. Social: 02, Cozinha, M2/ Terreno: 350, M2 rea Construida: 210, Detalhes: - Salo Novo!\", Ref.: SA95828 Tipo: Salo Comercial Cidade: So Jos do Rio Preto - SP Bairro: Jardim Mugnani,https://thumbs4.properati.com/5/ShU0-2rvOmZQkWG3Ml57SSATz_s=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2094/VmmbZZ9w1FykW0ldwpGM.jpg\n3931096ab81c0a74496858654375447e859df8f2,2014-12-05,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.7910821,-49.3781115\",-20.7910821,-49.3781115,880,BRL,695.77,225.33,,,,,,1,,http://www.properati.com.br/ue04_alugar_apartamentos_sao-jose-do-rio-preto_rua-pedro-mulati,\"Ref.: AP95815Tipo: ApartamentoCidade: So Jos do Rio Preto - SPBairro: Jardim ConceioCobertura, Dormitrios: 02, C/ A/e, Wc. Social, Box Wcs., Sala de Estar, Cozinha, Piscina, rea de Servio, rea de Lazer, Garagem Coberta: 01, rea Util M2: 65, Playground\",Ref.: AP95815 Tipo: Apartamento Cidade: So Jos do Rio Preto - SP Bairro: Jardim Conceio,https://thumbs4.properati.com/3/0zqbkQ73LUnQeQMp0yntvzoZrq8=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2458/Dv0976z6fjHNbJf7oAX0.jpg\nc15e079e557ec6ad9d86baede07324e678686764,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,3800,BRL,3604.66,1167.39,,88,13.265795454545456,43.18181818181818,,,,http://www.properati.com.br/ifzr_alugar_comercial_saude,\"\tConjunto Comercial proximo do Metrô São Judas, com 76m², 2 banheiros, 2 vagas, 1 copa, predio 24 horas. Referíªncia: CO243\",\"Comercio no(a) Saúde , São Paulo - SP\",https://thumbs4.properati.com/4/h5_FesPCfkGnmz5ac8c3DvXy_cE=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/243/68c5314b-39b1-4ebd-af49-694d915540b0.jpg\n62cbd166c6e120af08202b4db367db83240ed7e1,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,8500,BRL,8063.11,2611.28,,300,8.704266666666667,28.333333333333332,,,,http://www.properati.com.br/ifw2_alugar_comercial_vila-olimpia,\"\t\t\tLindo(a) Comercio de 300 metros quadrados no bairro Vila Olímpia na cidade de São Paulo - SP.\t\t\t, 2 banheiro(s), 6 vagas(s) de garagem.\t \t\tCondições de Pagamento: \t \t \t \t\tCaracteristicas: Referíªncia: CO93\",\"Comercio no(a) Vila Olímpia , São Paulo - SP\",https://thumbs4.properati.com/1/gH6VwXVOHsfAy5JV8elaJg59lGA=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/93/4276fcba-994a-4296-9a35-65c580711457.jpg\n5a5bfb194fdbec1a633f5894a66c8bf45c8ae463,2015-01-06,rent,apartment,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,1100,BRL,869.7,281.66,,,,,,,,http://www.properati.com.br/vv2s_alugar_apartamentos_sao-jose-do-rio-preto,\"Ref.: AP95935Tipo: ApartamentoCidade: So Jos do Rio Preto - SPBairro: Vila Itlia..: Apartamento Residencial Na Vila Itlia, Apartamento, rea Util M2: 63, Dormitrios: 02, Closet , Tipo Apto.: 01, Wc. Social: 01, Sala de Tv, Cozinha: 01, Cozinha C/ A/e, Garagem Coberta: 01, Detalhes: Prdio Novo.\",Ref.: AP95935 Tipo: Apartamento Cidade: So Jos do Rio Preto - SP Bairro: Vila Itlia,https://thumbs4.properati.com/5/XPnfndqmnEKY04smTHRwdeanGd8=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2535/atPPaTZcVegFbIYjH45d.jpg\n8f0adde258e0b9ad5897ba55288b52005ca749a8,2014-07-16,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,,,,3200,BRL,3035.52,983.07,,100,9.8307,32,,,,http://www.properati.com.br/ifxp_alugar_comercial_republica,\"\tLoja/Galpão com mezanino, pé direito duplo, localizada na Av. Amaral Guegel, proximo da Consolação, 2 banheiro e 2 vagas demarcadas. Referíªncia: CO169\",\"Comercio no(a) República , São Paulo - SP\",https://thumbs4.properati.com/6/XJyKobKfgKlfwiO0SB2Tt2i0Y9w=/trim/198x0/smart/filters:strip_icc()/souzapinto.imobfusion01.com.br/accounts/souzapinto/data/photo/169/50b1ac29-6a0b-4488-aaa4-07bc131dd969.jpg\n480229dcb811d916f90a02ffd9c5b62e8a72ccbe,2014-10-15,rent,store,São Paulo,|Brasil|São Paulo|,Brasil,São Paulo,,\"-20.8168432,-49.3930989\",-20.8168432,-49.3930989,17000,BRL,13441.34,4353.05,,,,,,,,http://www.properati.com.br/re16_alugar_comercial_sao-jose-do-rio-preto,\"Ref.: CO95289Tipo: Predio ComercialCidade: So Jos do Rio Preto - SPBairro: Vila dos BancariosM2 rea Construida: 800, Garagem Descoberta: 20, Detalhes: Predio Comercial Com 10m Salas de 80 M Cada, Fica Em Um Pavimento Com Elevador, 20 Vagas Para Estacionar .\",Loja,https://thumbs4.properati.com/4/Z-A_sLSPQOFzDbPb8MkkcY9IwMU=/trim/198x0/smart/filters:strip_icc()/static.if2.com.br/acc/interplan/photos/2239/bsfsEYepcv9Cu4EHDvjU.jpg\n\n", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_content" } - ], - "schemaVersion": 32, - "tags": [ - "gdev", - "panel-tests", - "table" - ], - "templating": { - "list": [] + ], + "title": "No pagination, no footer", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "country_name": true, + "created_on": true, + "expenses": true, + "floor": true, + "geonames_id": true, + "id": true, + "image_thumbnail": true, + "lat": true, + "lat_lon": true, + "lon": true, + "operation": true, + "place_with_parent_names": true, + "price_aprox_local_currency": true, + "price_aprox_usd": true, + "state_name": true, + "surface_total_in_m2": true + }, + "indexByName": { + "country_name": 13, + "created_on": 9, + "currency": 19, + "description": 5, + "expenses": 24, + "floor": 7, + "geonames_id": 15, + "id": 0, + "image_thumbnail": 26, + "lat": 17, + "lat_lon": 16, + "lon": 18, + "operation": 8, + "place_name": 4, + "place_with_parent_names": 12, + "price": 11, + "price_aprox_local_currency": 20, + "price_aprox_usd": 21, + "price_per_m2": 23, + "price_usd_per_m2": 22, + "properati_url": 25, + "property_type": 2, + "rooms": 6, + "state_name": 14, + "surface_covered_in_m2": 3, + "surface_total_in_m2": 10, + "title": 1 + }, + "renameByName": { + "description": "Description", + "place_name": "City", + "price": "Price", + "properati_url": "URL", + "property_type": "Type", + "rooms": "Rooms", + "surface_covered_in_m2": "Surface", + "title": "Property name" + } + } + } + ], + "type": "table" }, - "time": { - "from": "now-6h", - "to": "now" + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [ + { + "options": { + "rent": { + "color": "yellow", + "index": 0 + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "property_type" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "store": { + "color": "blue", + "index": 0 + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "title" + }, + "properties": [ + { + "id": "custom.width", + "value": 205 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Number of rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 66 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.inspect", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "URL" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Show details", + "url": "${__value.raw}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Surface" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background-solid" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "orange", + "value": 100 + }, + { + "color": "yellow", + "value": 200 + }, + { + "color": "green", + "value": 300 + }, + { + "color": "#EAB839", + "value": 500 + }, + { + "color": "#6ED0E0", + "value": 1000 + }, + { + "color": "#EF843C", + "value": 2000 + } + ] + } + }, + { + "id": "unit", + "value": "areaM2" + }, + { + "id": "custom.width", + "value": 93 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Type" + }, + "properties": [ + { + "id": "custom.width", + "value": 94 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "City" + }, + "properties": [ + { + "id": "custom.width", + "value": 120 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 77 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Price" + }, + "properties": [ + { + "id": "unit", + "value": "currencyUSD" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 0 + }, + "id": 6, + "options": { + "footer": { + "enablePagination": false, + "fields": "", + "reducer": ["sum"], + "show": true + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Surface" + } + ] + }, + "pluginVersion": "9.0.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "title": "No pagination, footer", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "country_name": true, + "created_on": true, + "expenses": true, + "floor": true, + "geonames_id": true, + "id": true, + "image_thumbnail": true, + "lat": true, + "lat_lon": true, + "lon": true, + "operation": true, + "place_with_parent_names": true, + "price_aprox_local_currency": true, + "price_aprox_usd": true, + "state_name": true, + "surface_total_in_m2": true + }, + "indexByName": { + "country_name": 13, + "created_on": 9, + "currency": 19, + "description": 5, + "expenses": 24, + "floor": 7, + "geonames_id": 15, + "id": 0, + "image_thumbnail": 26, + "lat": 17, + "lat_lon": 16, + "lon": 18, + "operation": 8, + "place_name": 4, + "place_with_parent_names": 12, + "price": 11, + "price_aprox_local_currency": 20, + "price_aprox_usd": 21, + "price_per_m2": 23, + "price_usd_per_m2": 22, + "properati_url": 25, + "property_type": 2, + "rooms": 6, + "state_name": 14, + "surface_covered_in_m2": 3, + "surface_total_in_m2": 10, + "title": 1 + }, + "renameByName": { + "description": "Description", + "place_name": "City", + "price": "Price", + "properati_url": "URL", + "property_type": "Type", + "rooms": "Rooms", + "surface_covered_in_m2": "Surface", + "title": "Property name" + } + } + } + ], + "type": "table" }, - "timepicker": {}, - "timezone": "", - "title": "Panel Tests - Table - Pagination", - "uid": "1YGJxPUnz", - "version": 3, - "weekStart": "" + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [ + { + "options": { + "rent": { + "color": "yellow", + "index": 0 + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "property_type" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "store": { + "color": "blue", + "index": 0 + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "title" + }, + "properties": [ + { + "id": "custom.width", + "value": 205 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Number of rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 66 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.inspect", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "URL" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Show details", + "url": "${__value.raw}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Surface" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background-solid" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "orange", + "value": 100 + }, + { + "color": "yellow", + "value": 200 + }, + { + "color": "green", + "value": 300 + }, + { + "color": "#EAB839", + "value": 500 + }, + { + "color": "#6ED0E0", + "value": 1000 + }, + { + "color": "#EF843C", + "value": 2000 + } + ] + } + }, + { + "id": "unit", + "value": "areaM2" + }, + { + "id": "custom.width", + "value": 93 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Type" + }, + "properties": [ + { + "id": "custom.width", + "value": 94 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "City" + }, + "properties": [ + { + "id": "custom.width", + "value": 120 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 77 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Price" + }, + "properties": [ + { + "id": "unit", + "value": "currencyUSD" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 0 + }, + "id": 9, + "options": { + "footer": { + "enablePagination": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": false, + "sortBy": [ + { + "desc": true, + "displayName": "Surface" + } + ] + }, + "pluginVersion": "9.0.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "country_name": true, + "created_on": true, + "expenses": true, + "floor": true, + "geonames_id": true, + "id": true, + "image_thumbnail": true, + "lat": true, + "lat_lon": true, + "lon": true, + "operation": true, + "place_with_parent_names": true, + "price_aprox_local_currency": true, + "price_aprox_usd": true, + "state_name": true, + "surface_total_in_m2": true + }, + "indexByName": { + "country_name": 13, + "created_on": 9, + "currency": 19, + "description": 5, + "expenses": 24, + "floor": 7, + "geonames_id": 15, + "id": 0, + "image_thumbnail": 26, + "lat": 17, + "lat_lon": 16, + "lon": 18, + "operation": 8, + "place_name": 4, + "place_with_parent_names": 12, + "price": 11, + "price_aprox_local_currency": 20, + "price_aprox_usd": 21, + "price_per_m2": 23, + "price_usd_per_m2": 22, + "properati_url": 25, + "property_type": 2, + "rooms": 6, + "state_name": 14, + "surface_covered_in_m2": 3, + "surface_total_in_m2": 10, + "title": 1 + }, + "renameByName": { + "description": "Description", + "place_name": "City", + "price": "Price", + "properati_url": "URL", + "property_type": "Type", + "rooms": "Rooms", + "surface_covered_in_m2": "Surface", + "title": "Property name" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [ + { + "options": { + "rent": { + "color": "yellow", + "index": 0 + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "property_type" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "store": { + "color": "blue", + "index": 0 + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "title" + }, + "properties": [ + { + "id": "custom.width", + "value": 205 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Number of rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 66 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.inspect", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "URL" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Show details", + "url": "${__value.raw}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Surface" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background-solid" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "orange", + "value": 100 + }, + { + "color": "yellow", + "value": 200 + }, + { + "color": "green", + "value": 300 + }, + { + "color": "#EAB839", + "value": 500 + }, + { + "color": "#6ED0E0", + "value": 1000 + }, + { + "color": "#EF843C", + "value": 2000 + } + ] + } + }, + { + "id": "unit", + "value": "areaM2" + }, + { + "id": "custom.width", + "value": 93 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Type" + }, + "properties": [ + { + "id": "custom.width", + "value": 94 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "City" + }, + "properties": [ + { + "id": "custom.width", + "value": 120 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 77 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Price" + }, + "properties": [ + { + "id": "unit", + "value": "currencyUSD" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 7, + "options": { + "footer": { + "enablePagination": true, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Surface" + } + ] + }, + "pluginVersion": "9.0.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "title": "Pagination, no footer", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "country_name": true, + "created_on": true, + "expenses": true, + "floor": true, + "geonames_id": true, + "id": true, + "image_thumbnail": true, + "lat": true, + "lat_lon": true, + "lon": true, + "operation": true, + "place_with_parent_names": true, + "price_aprox_local_currency": true, + "price_aprox_usd": true, + "state_name": true, + "surface_total_in_m2": true + }, + "indexByName": { + "country_name": 13, + "created_on": 9, + "currency": 19, + "description": 5, + "expenses": 24, + "floor": 7, + "geonames_id": 15, + "id": 0, + "image_thumbnail": 26, + "lat": 17, + "lat_lon": 16, + "lon": 18, + "operation": 8, + "place_name": 4, + "place_with_parent_names": 12, + "price": 11, + "price_aprox_local_currency": 20, + "price_aprox_usd": 21, + "price_per_m2": 23, + "price_usd_per_m2": 22, + "properati_url": 25, + "property_type": 2, + "rooms": 6, + "state_name": 14, + "surface_covered_in_m2": 3, + "surface_total_in_m2": 10, + "title": 1 + }, + "renameByName": { + "description": "Description", + "place_name": "City", + "price": "Price", + "properati_url": "URL", + "property_type": "Type", + "rooms": "Rooms", + "surface_covered_in_m2": "Surface", + "title": "Property name" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [ + { + "options": { + "rent": { + "color": "yellow", + "index": 0 + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "property_type" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "store": { + "color": "blue", + "index": 0 + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "title" + }, + "properties": [ + { + "id": "custom.width", + "value": 205 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Number of rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 66 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.inspect", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "URL" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Show details", + "url": "${__value.raw}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Surface" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background-solid" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "orange", + "value": 100 + }, + { + "color": "yellow", + "value": 200 + }, + { + "color": "green", + "value": 300 + }, + { + "color": "#EAB839", + "value": 500 + }, + { + "color": "#6ED0E0", + "value": 1000 + }, + { + "color": "#EF843C", + "value": 2000 + } + ] + } + }, + { + "id": "unit", + "value": "areaM2" + }, + { + "id": "custom.width", + "value": 93 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Type" + }, + "properties": [ + { + "id": "custom.width", + "value": 94 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "City" + }, + "properties": [ + { + "id": "custom.width", + "value": 120 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 77 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Price" + }, + "properties": [ + { + "id": "unit", + "value": "currencyUSD" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 8, + "options": { + "footer": { + "enablePagination": true, + "fields": "", + "reducer": ["sum"], + "show": true + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Surface" + } + ] + }, + "pluginVersion": "9.0.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "title": "Pagination and footer", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "country_name": true, + "created_on": true, + "expenses": true, + "floor": true, + "geonames_id": true, + "id": true, + "image_thumbnail": true, + "lat": true, + "lat_lon": true, + "lon": true, + "operation": true, + "place_with_parent_names": true, + "price_aprox_local_currency": true, + "price_aprox_usd": true, + "state_name": true, + "surface_total_in_m2": true + }, + "indexByName": { + "country_name": 13, + "created_on": 9, + "currency": 19, + "description": 5, + "expenses": 24, + "floor": 7, + "geonames_id": 15, + "id": 0, + "image_thumbnail": 26, + "lat": 17, + "lat_lon": 16, + "lon": 18, + "operation": 8, + "place_name": 4, + "place_with_parent_names": 12, + "price": 11, + "price_aprox_local_currency": 20, + "price_aprox_usd": 21, + "price_per_m2": 23, + "price_usd_per_m2": 22, + "properati_url": 25, + "property_type": 2, + "rooms": 6, + "state_name": 14, + "surface_covered_in_m2": 3, + "surface_total_in_m2": 10, + "title": 1 + }, + "renameByName": { + "description": "Description", + "place_name": "City", + "price": "Price", + "properati_url": "URL", + "property_type": "Type", + "rooms": "Rooms", + "surface_covered_in_m2": "Surface", + "title": "Property name" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [ + { + "options": { + "rent": { + "color": "yellow", + "index": 0 + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "property_type" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "store": { + "color": "blue", + "index": 0 + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "title" + }, + "properties": [ + { + "id": "custom.width", + "value": 205 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Number of rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 66 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.inspect", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "URL" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Show details", + "url": "${__value.raw}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Surface" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background-solid" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "orange", + "value": 100 + }, + { + "color": "yellow", + "value": 200 + }, + { + "color": "green", + "value": 300 + }, + { + "color": "#EAB839", + "value": 500 + }, + { + "color": "#6ED0E0", + "value": 1000 + }, + { + "color": "#EF843C", + "value": 2000 + } + ] + } + }, + { + "id": "unit", + "value": "areaM2" + }, + { + "id": "custom.width", + "value": 93 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Type" + }, + "properties": [ + { + "id": "custom.width", + "value": 94 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "City" + }, + "properties": [ + { + "id": "custom.width", + "value": 120 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 77 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Price" + }, + "properties": [ + { + "id": "unit", + "value": "currencyUSD" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 16 + }, + "id": 10, + "options": { + "footer": { + "enablePagination": true, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": false, + "sortBy": [ + { + "desc": true, + "displayName": "Surface" + } + ] + }, + "pluginVersion": "9.0.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "title": "Pagination, no footer, no header", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "country_name": true, + "created_on": true, + "expenses": true, + "floor": true, + "geonames_id": true, + "id": true, + "image_thumbnail": true, + "lat": true, + "lat_lon": true, + "lon": true, + "operation": true, + "place_with_parent_names": true, + "price_aprox_local_currency": true, + "price_aprox_usd": true, + "state_name": true, + "surface_total_in_m2": true + }, + "indexByName": { + "country_name": 13, + "created_on": 9, + "currency": 19, + "description": 5, + "expenses": 24, + "floor": 7, + "geonames_id": 15, + "id": 0, + "image_thumbnail": 26, + "lat": 17, + "lat_lon": 16, + "lon": 18, + "operation": 8, + "place_name": 4, + "place_with_parent_names": 12, + "price": 11, + "price_aprox_local_currency": 20, + "price_aprox_usd": 21, + "price_per_m2": 23, + "price_usd_per_m2": 22, + "properati_url": 25, + "property_type": 2, + "rooms": 6, + "state_name": 14, + "surface_covered_in_m2": 3, + "surface_total_in_m2": 10, + "title": 1 + }, + "renameByName": { + "description": "Description", + "place_name": "City", + "price": "Price", + "properati_url": "URL", + "property_type": "Type", + "rooms": "Rooms", + "surface_covered_in_m2": "Surface", + "title": "Property name" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [ + { + "options": { + "rent": { + "color": "yellow", + "index": 0 + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "property_type" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "store": { + "color": "blue", + "index": 0 + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "title" + }, + "properties": [ + { + "id": "custom.width", + "value": 205 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Number of rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 66 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.inspect", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "URL" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Show details", + "url": "${__value.raw}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Surface" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background-solid" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "orange", + "value": 100 + }, + { + "color": "yellow", + "value": 200 + }, + { + "color": "green", + "value": 300 + }, + { + "color": "#EAB839", + "value": 500 + }, + { + "color": "#6ED0E0", + "value": 1000 + }, + { + "color": "#EF843C", + "value": 2000 + } + ] + } + }, + { + "id": "unit", + "value": "areaM2" + }, + { + "id": "custom.width", + "value": 93 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Type" + }, + "properties": [ + { + "id": "custom.width", + "value": 94 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "City" + }, + "properties": [ + { + "id": "custom.width", + "value": 120 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 77 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Price" + }, + "properties": [ + { + "id": "unit", + "value": "currencyUSD" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 16 + }, + "id": 11, + "options": { + "footer": { + "enablePagination": true, + "fields": "", + "reducer": ["sum"], + "show": true + }, + "showHeader": false, + "sortBy": [ + { + "desc": true, + "displayName": "Surface" + } + ] + }, + "pluginVersion": "9.0.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "title": "Pagination, footer, no header", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "country_name": true, + "created_on": true, + "expenses": true, + "floor": true, + "geonames_id": true, + "id": true, + "image_thumbnail": true, + "lat": true, + "lat_lon": true, + "lon": true, + "operation": true, + "place_with_parent_names": true, + "price_aprox_local_currency": true, + "price_aprox_usd": true, + "state_name": true, + "surface_total_in_m2": true + }, + "indexByName": { + "country_name": 13, + "created_on": 9, + "currency": 19, + "description": 5, + "expenses": 24, + "floor": 7, + "geonames_id": 15, + "id": 0, + "image_thumbnail": 26, + "lat": 17, + "lat_lon": 16, + "lon": 18, + "operation": 8, + "place_name": 4, + "place_with_parent_names": 12, + "price": 11, + "price_aprox_local_currency": 20, + "price_aprox_usd": 21, + "price_per_m2": 23, + "price_usd_per_m2": 22, + "properati_url": 25, + "property_type": 2, + "rooms": 6, + "state_name": 14, + "surface_covered_in_m2": 3, + "surface_total_in_m2": 10, + "title": 1 + }, + "renameByName": { + "description": "Description", + "place_name": "City", + "price": "Price", + "properati_url": "URL", + "property_type": "Type", + "rooms": "Rooms", + "surface_covered_in_m2": "Surface", + "title": "Property name" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [ + { + "options": { + "rent": { + "color": "yellow", + "index": 0 + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "property_type" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "options": { + "store": { + "color": "blue", + "index": 0 + } + }, + "type": "value" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "title" + }, + "properties": [ + { + "id": "custom.width", + "value": 205 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Number of rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 66 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Description" + }, + "properties": [ + { + "id": "custom.inspect", + "value": true + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "URL" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Show details", + "url": "${__value.raw}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Surface" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background-solid" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "orange", + "value": 100 + }, + { + "color": "yellow", + "value": 200 + }, + { + "color": "green", + "value": 300 + }, + { + "color": "#EAB839", + "value": 500 + }, + { + "color": "#6ED0E0", + "value": 1000 + }, + { + "color": "#EF843C", + "value": 2000 + } + ] + } + }, + { + "id": "unit", + "value": "areaM2" + }, + { + "id": "custom.width", + "value": 93 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Type" + }, + "properties": [ + { + "id": "custom.width", + "value": 94 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "City" + }, + "properties": [ + { + "id": "custom.width", + "value": 120 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Rooms" + }, + "properties": [ + { + "id": "custom.width", + "value": 77 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Price" + }, + "properties": [ + { + "id": "unit", + "value": "currencyUSD" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 16 + }, + "id": 12, + "options": { + "footer": { + "enablePagination": true, + "fields": "", + "reducer": ["sum"], + "show": true + }, + "showHeader": false, + "sortBy": [ + { + "desc": true, + "displayName": "Surface" + } + ] + }, + "pluginVersion": "9.0.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": { + "country_name": true, + "created_on": true, + "expenses": true, + "floor": true, + "geonames_id": true, + "id": true, + "image_thumbnail": true, + "lat": true, + "lat_lon": true, + "lon": true, + "operation": true, + "place_with_parent_names": true, + "price_aprox_local_currency": true, + "price_aprox_usd": true, + "state_name": true, + "surface_total_in_m2": true + }, + "indexByName": { + "country_name": 13, + "created_on": 9, + "currency": 19, + "description": 5, + "expenses": 24, + "floor": 7, + "geonames_id": 15, + "id": 0, + "image_thumbnail": 26, + "lat": 17, + "lat_lon": 16, + "lon": 18, + "operation": 8, + "place_name": 4, + "place_with_parent_names": 12, + "price": 11, + "price_aprox_local_currency": 20, + "price_aprox_usd": 21, + "price_per_m2": 23, + "price_usd_per_m2": 22, + "properati_url": 25, + "property_type": 2, + "rooms": 6, + "state_name": 14, + "surface_covered_in_m2": 3, + "surface_total_in_m2": 10, + "title": 1 + }, + "renameByName": { + "description": "Description", + "place_name": "City", + "price": "Price", + "properati_url": "URL", + "property_type": "Type", + "rooms": "Rooms", + "surface_covered_in_m2": "Surface", + "title": "Property name" + } + } + } + ], + "type": "table" + } + ], + "schemaVersion": 32, + "tags": ["gdev", "panel-tests", "table"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Panel Tests - Table - Pagination", + "uid": "1YGJxPUnz", + "version": 3, + "weekStart": "" } diff --git a/devenv/dev-dashboards/panel-table/table_sparkline_cell.json b/devenv/dev-dashboards/panel-table/table_sparkline_cell.json index 24ec5a57326..d36609b1678 100644 --- a/devenv/dev-dashboards/panel-table/table_sparkline_cell.json +++ b/devenv/dev-dashboards/panel-table/table_sparkline_cell.json @@ -23,8 +23,7 @@ "panels": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -69,9 +68,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -80,8 +77,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -103,8 +99,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -151,9 +146,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -162,8 +155,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -185,8 +177,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -233,9 +224,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -244,8 +233,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -267,8 +255,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -317,9 +304,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -328,8 +313,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -351,8 +335,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -401,9 +384,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -412,8 +393,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -435,8 +415,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -487,9 +466,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -498,8 +475,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "ca3da78e-6bcc-4a2a-a080-d338a14528ca" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -521,8 +497,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -566,9 +541,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true diff --git a/devenv/dev-dashboards/panel-table/table_tests.json b/devenv/dev-dashboards/panel-table/table_tests.json index a8d0a0d5253..be4041d45d6 100644 --- a/devenv/dev-dashboards/panel-table/table_tests.json +++ b/devenv/dev-dashboards/panel-table/table_tests.json @@ -19,7 +19,9 @@ "panels": [ { "columns": [], - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fontSize": "100%", "gridPos": { "h": 11, @@ -114,7 +116,9 @@ "value": "current" } ], - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fontSize": "100%", "gridPos": { "h": 11, @@ -196,7 +200,9 @@ }, { "columns": [], - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fontSize": "100%", "gridPos": { "h": 7, @@ -260,7 +266,9 @@ }, { "columns": [], - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fontSize": "100%", "gridPos": { "h": 8, @@ -342,7 +350,9 @@ }, { "columns": [], - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fontSize": "100%", "gridPos": { "h": 10, @@ -431,11 +441,7 @@ ], "refresh": false, "schemaVersion": 16, - "tags": [ - "gdev", - "panel-tests", - "table" - ], + "tags": ["gdev", "panel-tests", "table"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-table/table_tests_new.json b/devenv/dev-dashboards/panel-table/table_tests_new.json index e928e9fc578..801b6e3fbd9 100644 --- a/devenv/dev-dashboards/panel-table/table_tests_new.json +++ b/devenv/dev-dashboards/panel-table/table_tests_new.json @@ -36,8 +36,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -139,9 +138,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true, @@ -157,8 +154,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -171,18 +167,16 @@ { "id": "reduce", "options": { - "reducers": [ - "max", - "mean", - "last" - ] + "reducers": ["max", "mean", "last"] } } ], "type": "table" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -276,9 +270,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true, @@ -294,8 +286,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk_table", @@ -329,7 +320,9 @@ "type": "table" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -423,9 +416,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true, @@ -436,8 +427,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "labels": "cluster=eu,service=checkout", "min": 0.1, @@ -466,7 +456,9 @@ "type": "table" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -598,9 +590,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true, @@ -610,8 +600,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "labels": "cluster=eu,service=checkout", "min": 0.1, @@ -621,8 +610,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "hide": false, "labels": "cluster=eu,service=checkout", @@ -678,8 +666,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -762,9 +749,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true, @@ -775,8 +760,7 @@ { "alias": "S1", "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "labels": "server=A", "refId": "A", @@ -787,8 +771,7 @@ { "alias": "S2", "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "labels": "server=B", "refId": "B", @@ -799,8 +782,7 @@ { "alias": "S3", "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "labels": "server=C", "refId": "C", @@ -820,8 +802,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -873,9 +854,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": false, @@ -891,8 +870,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk_table", @@ -925,8 +903,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -968,9 +945,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": true }, "showHeader": true, @@ -980,8 +955,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "refId": "A" } @@ -993,11 +967,7 @@ "preload": false, "refresh": "", "schemaVersion": 41, - "tags": [ - "gdev", - "panel-tests", - "table" - ], + "tags": ["gdev", "panel-tests", "table"], "templating": { "list": [] }, @@ -1006,17 +976,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - React Table", diff --git a/devenv/dev-dashboards/panel-text/text-options.json b/devenv/dev-dashboards/panel-text/text-options.json index 696bda08d52..bff4a12bfd0 100644 --- a/devenv/dev-dashboards/panel-text/text-options.json +++ b/devenv/dev-dashboards/panel-text/text-options.json @@ -29,7 +29,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 9, "w": 12, @@ -50,8 +52,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" @@ -61,7 +62,9 @@ "type": "text" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 9, "w": 12, @@ -82,8 +85,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" @@ -93,7 +95,9 @@ "type": "text" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 9, "w": 12, @@ -114,8 +118,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" @@ -125,7 +128,9 @@ "type": "text" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 9, "w": 12, @@ -146,8 +151,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" @@ -165,16 +169,11 @@ { "current": { "selected": false, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "definition": "*", "hide": 0, @@ -198,16 +197,11 @@ { "current": { "selected": false, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": ["All"], + "value": ["$__all"] }, "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "definition": "$datacenter.*", "hide": 0, @@ -231,18 +225,11 @@ { "current": { "selected": false, - "text": [ - "AAA", - "ACB" - ], - "value": [ - "AAA", - "ACB" - ] + "text": ["AAA", "ACB"], + "value": ["AAA", "ACB"] }, "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "definition": "$datacenter.$server.*", "hide": 0, @@ -289,17 +276,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Text options", diff --git a/devenv/dev-dashboards/panel-timeline/timeline-align-endtime.json b/devenv/dev-dashboards/panel-timeline/timeline-align-endtime.json index 8542c7349b6..67fe2391167 100644 --- a/devenv/dev-dashboards/panel-timeline/timeline-align-endtime.json +++ b/devenv/dev-dashboards/panel-timeline/timeline-align-endtime.json @@ -23,8 +23,7 @@ "panels": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -85,8 +84,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"typeVersion\": [\n 0,\n 0\n ]\n },\n \"name\": \"A\",\n \"fields\": [\n {\n \"name\": \"channel\",\n \"config\": {\n \"selector\": \"channel\"\n },\n \"type\": \"string\"\n },\n {\n \"name\": \"name\",\n \"config\": {\n \"selector\": \"name\"\n },\n \"type\": \"string\"\n },\n {\n \"name\": \"starttime\",\n \"config\": {\n \"selector\": \"starttime\"\n },\n \"type\": \"string\"\n },\n {\n \"name\": \"endtime\",\n \"config\": {\n \"selector\": \"endtime\"\n },\n \"type\": \"string\"\n },\n {\n \"name\": \"duration_minutes\",\n \"config\": {\n \"selector\": \"duration_minutes\"\n },\n \"type\": \"number\"\n },\n {\n \"name\": \"state\",\n \"config\": {\n \"selector\": \"state\"\n },\n \"type\": \"string\"\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n \"Channel 1\",\n \"Channel 2\",\n \"Channel 1\",\n \"Channel 2\"\n ],\n [\n \"Event 1\",\n \"Event 2\",\n \"Event 3\",\n \"Event 4\"\n ],\n [\n \"2024-02-28T08:00:00Z\",\n \"2024-02-28T09:00:00Z\",\n \"2024-02-28T11:00:00Z\",\n \"2024-02-28T12:30:00Z\"\n ],\n [\n \"2024-02-28T10:00:00Z\",\n \"2024-02-28T10:30:00Z\",\n \"2024-02-28T14:00:00Z\",\n \"2024-02-28T13:30:00Z\"\n ],\n [\n 120,\n 90,\n 180,\n 60\n ],\n [\n \"OK\",\n \"ERROR\",\n \"NO_DATA\",\n \"WARNING\"\n ]\n ]\n }\n }\n]", "refId": "A", @@ -131,12 +129,7 @@ { "destinationType": "enum", "enumConfig": { - "text": [ - "OK", - "ERROR", - "NO_DATA", - "WARNING" - ] + "text": ["OK", "ERROR", "NO_DATA", "WARNING"] }, "targetField": "state" } @@ -147,9 +140,7 @@ { "id": "partitionByValues", "options": { - "fields": [ - "channel" - ], + "fields": ["channel"], "keepFields": false, "naming": { "asLabels": false @@ -161,8 +152,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -225,8 +215,7 @@ "alias": "Channel 1", "csvContent": "starttime,endtime,state\n1709107200000,1709114400000,OK\n1709118000000,1709128800000,NO_DATA", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -235,8 +224,7 @@ "alias": "Channel 2", "csvContent": "starttime,endtime,state\n1709110800000,1709116200000,ERROR\n1709123400000,1709127000000,WARNING", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_content" @@ -248,12 +236,7 @@ ], "refresh": "", "schemaVersion": 39, - "tags": [ - "gdev", - "panel-tests", - "graph-ng", - "demo" - ], + "tags": ["gdev", "panel-tests", "graph-ng", "demo"], "templating": { "list": [] }, @@ -268,4 +251,4 @@ "uid": "cdf3gkge5reo0f", "version": 4, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-timeline/timeline-align-nulls-retain.json b/devenv/dev-dashboards/panel-timeline/timeline-align-nulls-retain.json index 49907cca771..8d2f86bcdd7 100644 --- a/devenv/dev-dashboards/panel-timeline/timeline-align-nulls-retain.json +++ b/devenv/dev-dashboards/panel-timeline/timeline-align-nulls-retain.json @@ -23,8 +23,7 @@ "panels": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -176,8 +175,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"Dose\",\n \"meta\": {\n \"executedQueryString\": \"from(bucket: \\\"data\\\")\\r\\n |> range(start: 2023-10-20T05:04:00Z, stop: 2023-10-20T07:22:00Z)\\r\\n |> filter(fn: (r) => r[\\\"_field\\\"] == \\\"FactoryManager_Analogs_DB.A01C02U09.PHS.Dose\\\")\\r\\n |> keep(columns: [\\\"_time\\\", \\\"_value\\\"])\\r\\n |> map(fn: (r) => ({ \\r\\n \\\"Dose\\\": r._value,\\r\\n time: r._time,\\r\\n }))\\r\\n\",\n \"typeVersion\": [\n 0,\n 0\n ]\n },\n \"fields\": [\n {\n \"config\": {},\n \"labels\": {},\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {},\n \"name\": \"Dose\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1697781872300,\n 1697781963303,\n 1697784138453,\n 1697784160451\n ],\n [\n \"Cold Water Dosing Active (150 ltrs)\",\n null,\n \"Hot Water Dosing Active (50 ltrs)\",\n null\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"Mix\",\n \"meta\": {\n \"executedQueryString\": \"from(bucket: \\\"data\\\")\\r\\n |> range(start: 2023-10-20T05:04:00Z, stop: 2023-10-20T07:22:00Z)\\r\\n |> filter(fn: (r) => r[\\\"_field\\\"] == \\\"FactoryManager_Analogs_DB.A01C02U09.PHS.Mix\\\")\\r\\n |> keep(columns: [\\\"_time\\\", \\\"_value\\\"])\\r\\n |> map(fn: (r) => ({ \\r\\n \\\"Mix\\\": r._value,\\r\\n time: r._time,\\r\\n }))\\r\\n\",\n \"typeVersion\": [\n 0,\n 0\n ]\n },\n \"fields\": [\n {\n \"config\": {},\n \"labels\": {},\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {},\n \"name\": \"Mix\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1697778291972,\n 1697778393992,\n 1697778986994,\n 1697786485890\n ],\n [\n \"Running Constant Forward\",\n null,\n \"Running Constant Forward\",\n null\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"Cook\",\n \"meta\": {\n \"executedQueryString\": \"from(bucket: \\\"data\\\")\\r\\n |> range(start: 2023-10-20T05:04:00Z, stop: 2023-10-20T07:22:00Z)\\r\\n |> filter(fn: (r) => r[\\\"_field\\\"] == \\\"FactoryManager_Analogs_DB.A01C02U09.PHS.Cook\\\")\\r\\n |> keep(columns: [\\\"_time\\\", \\\"_value\\\"])\\r\\n |> map(fn: (r) => ({ \\r\\n \\\"Cook\\\": r._value,\\r\\n time: r._time,\\r\\n }))\\r\\n\",\n \"typeVersion\": [\n 0,\n 0\n ]\n },\n \"fields\": [\n {\n \"config\": {},\n \"labels\": {},\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {},\n \"name\": \"Cook\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1697779163986,\n 1697779921045,\n 1697780221094,\n 1697780521111,\n 1697781186192,\n 1697781786291,\n 1697783332361,\n 1697783784395,\n 1697783790397,\n 1697784146478,\n 1697784517471,\n 1697784523487,\n 1697784949480,\n 1697785369505\n ],\n [\n \"Heating to Setpoint (92c)\",\n \"Stage Time Running (5 mins)\",\n null,\n \"Heating to Setpoint (96c)\",\n \"Stage Time Running (10 mins)\",\n null,\n \"Heating to Setpoint (92c)\",\n \"Stage Time Running (0 mins)\",\n null,\n \"Heating to Setpoint (92c)\",\n \"Stage Time Running (0 mins)\",\n null,\n \"CCP in Progress (7 mins)\",\n null\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"Shear\",\n \"meta\": {\n \"executedQueryString\": \"from(bucket: \\\"data\\\")\\r\\n |> range(start: 2023-10-20T05:04:00Z, stop: 2023-10-20T07:22:00Z)\\r\\n |> filter(fn: (r) => r[\\\"_field\\\"] == \\\"FactoryManager_Analogs_DB.A01C02U09.PHS.Shear\\\")\\r\\n |> keep(columns: [\\\"_time\\\", \\\"_value\\\"])\\r\\n |> map(fn: (r) => ({ \\r\\n \\\"Int. Shear\\\": r._value,\\r\\n time: r._time,\\r\\n }))\\r\\n\",\n \"typeVersion\": [\n 0,\n 0\n ]\n },\n \"fields\": [\n {\n \"config\": {},\n \"labels\": {},\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {},\n \"name\": \"Int. Shear\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1697782100330,\n 1697782832342\n ],\n [\n \"Shearing Active (12 mins)\",\n null\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"Recirc\",\n \"meta\": {\n \"executedQueryString\": \"from(bucket: \\\"data\\\")\\r\\n |> range(start: 2023-10-20T05:04:00Z, stop: 2023-10-20T07:22:00Z)\\r\\n |> filter(fn: (r) => r[\\\"_field\\\"] == \\\"FactoryManager_Analogs_DB.A01C02U09.PHS.Recirc\\\")\\r\\n |> keep(columns: [\\\"_time\\\", \\\"_value\\\"])\\r\\n |> map(fn: (r) => ({ \\r\\n \\\"Ext. Shear\\\": r._value,\\r\\n time: r._time,\\r\\n }))\\r\\n\",\n \"typeVersion\": [\n 0,\n 0\n ]\n },\n \"fields\": []\n },\n \"data\": {\n \"values\": []\n }\n },\n {\n \"schema\": {\n \"refId\": \"Transfer\",\n \"meta\": {\n \"executedQueryString\": \"from(bucket: \\\"data\\\")\\r\\n |> range(start: 2023-10-20T05:04:00Z, stop: 2023-10-20T07:22:00Z)\\r\\n |> filter(fn: (r) => r[\\\"_field\\\"] == \\\"FactoryManager_Analogs_DB.A01C02U09.PHS.Transfer\\\")\\r\\n |> keep(columns: [\\\"_time\\\", \\\"_value\\\"])\\r\\n |> map(fn: (r) => ({ \\r\\n \\\"Transfer\\\": r._value,\\r\\n time: r._time,\\r\\n }))\\r\\n\",\n \"typeVersion\": [\n 0,\n 0\n ]\n },\n \"fields\": [\n {\n \"config\": {},\n \"labels\": {},\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {},\n \"name\": \"Transfer\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1697785713869,\n 1697785753879,\n 1697785764887,\n 1697785875872,\n 1697786481929\n ],\n [\n \"Pre-Start Drain\",\n null,\n \"Build Pressure (0.6 Barg)\",\n \"Transfer in progress (0.7 Barg)\",\n \"Wait for pressure dissipation (0.2 Barg)\"\n ]\n ]\n }\n }\n]", "refId": "A", @@ -190,12 +188,7 @@ ], "refresh": "", "schemaVersion": 39, - "tags": [ - "gdev", - "panel-tests", - "graph-ng", - "demo" - ], + "tags": ["gdev", "panel-tests", "graph-ng", "demo"], "templating": { "list": [] }, @@ -210,4 +203,4 @@ "uid": "edf55caay3w8wa", "version": 4, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-timeline/timeline-demo.json b/devenv/dev-dashboards/panel-timeline/timeline-demo.json index e25aa23ce1c..37daf4e71f1 100644 --- a/devenv/dev-dashboards/panel-timeline/timeline-demo.json +++ b/devenv/dev-dashboards/panel-timeline/timeline-demo.json @@ -29,7 +29,7 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -128,7 +128,7 @@ "type": "state-timeline" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -231,7 +231,7 @@ "type": "state-timeline" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "description": "Should show gaps", "fieldConfig": { "defaults": { @@ -334,7 +334,7 @@ "type": "state-timeline" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -428,12 +428,7 @@ ], "refresh": false, "schemaVersion": 36, - "tags": [ - "gdev", - "panel-tests", - "graph-ng", - "demo" - ], + "tags": ["gdev", "panel-tests", "graph-ng", "demo"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-timeline/timeline-modes.json b/devenv/dev-dashboards/panel-timeline/timeline-modes.json index 7c4d9ee05c3..348f6b97b62 100644 --- a/devenv/dev-dashboards/panel-timeline/timeline-modes.json +++ b/devenv/dev-dashboards/panel-timeline/timeline-modes.json @@ -29,7 +29,7 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -89,26 +89,11 @@ }, "lines": 10, "points": [ - [ - 0, - 1616551651000 - ], - [ - 1, - 1616556554000 - ], - [ - 2, - 1616559873000 - ], - [ - 0, - 1616561077000 - ], - [ - 3, - 1616563090000 - ] + [0, 1616551651000], + [1, 1616556554000], + [2, 1616559873000], + [0, 1616561077000], + [3, 1616563090000] ], "pulseWave": { "offCount": 3, @@ -137,22 +122,10 @@ "hide": false, "lines": 10, "points": [ - [ - 4, - 1616555060000 - ], - [ - 5, - 1616560081000 - ], - [ - 4, - 1616562217000 - ], - [ - 5, - 1616565458000 - ] + [4, 1616555060000], + [5, 1616560081000], + [4, 1616562217000], + [5, 1616565458000] ], "pulseWave": { "offCount": 3, @@ -174,31 +147,13 @@ }, { "points": [ - [ - 4, - 1616557148000 - ], - [ - 1616558756000 - ], - [ - 4, - 1616561658000 - ], - [ - 1616562446000 - ], - [ - 4, - 1616564104000 - ], - [ - 1616564548000 - ], - [ - 4, - 1616564871000 - ] + [4, 1616557148000], + [1616558756000], + [4, 1616561658000], + [1616562446000], + [4, 1616564104000], + [1616564548000], + [4, 1616564871000] ], "refId": "C", "scenarioId": "manual_entry" @@ -208,7 +163,7 @@ "type": "state-timeline" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -282,7 +237,7 @@ "type": "state-timeline" }, { - "datasource": { "type": "testdata" }, + "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { "color": { @@ -372,11 +327,7 @@ ], "refresh": false, "schemaVersion": 36, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-timeline/timeline-thresholds-mappings.json b/devenv/dev-dashboards/panel-timeline/timeline-thresholds-mappings.json index d1f0d745117..2a196670bc1 100644 --- a/devenv/dev-dashboards/panel-timeline/timeline-thresholds-mappings.json +++ b/devenv/dev-dashboards/panel-timeline/timeline-thresholds-mappings.json @@ -29,7 +29,7 @@ "panels": [ { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -92,8 +92,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -105,7 +104,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -176,8 +175,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -189,7 +187,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -260,8 +258,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -273,7 +270,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -369,8 +366,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -382,7 +378,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -486,8 +482,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -499,7 +494,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -616,8 +611,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -629,7 +623,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -692,8 +686,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"int64\",\n \"nullable\": true\n },\n \"config\": {\n \"thresholds\": {\n \"mode\": \"absolute\",\n \"steps\": [\n {\n \"color\": \"green\",\n \"value\": null\n },\n {\n \"color\": \"#EAB839\",\n \"value\": 10\n },\n {\n \"color\": \"red\",\n \"value\": 15\n },\n {\n \"color\": \"#6ED0E0\",\n \"value\": 25\n }\n ]\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1674732835000,\n 1674736435000,\n 1674740035000,\n 1674743635000\n ],\n [\n 5,\n 10,\n 20,\n 30\n ]\n ]\n }\n }\n]", "refId": "A", @@ -705,7 +698,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -768,8 +761,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -777,8 +769,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "max": 30, @@ -808,7 +799,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -891,8 +882,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"int64\",\n \"nullable\": true\n },\n \"config\": {\n \"thresholds\": {\n \"mode\": \"absolute\",\n \"steps\": [\n {\n \"color\": \"green\",\n \"value\": null\n }\n ]\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1674732835000,\n 1674736435000,\n 1674740035000,\n 1674743635000,\n 1674747235000\n ],\n [\n 5,\n null,\n 20,\n null,\n 40\n ]\n ],\n \"entities\": [null, { \"NaN\": [3]}]\n }\n }\n]", "refId": "A", @@ -904,7 +894,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -976,8 +966,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"int64\",\n \"nullable\": true\n },\n \"config\": {\n \"thresholds\": {\n \"mode\": \"absolute\",\n \"steps\": [\n {\n \"color\": \"green\",\n \"value\": null\n }\n ]\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1674732835000,\n 1674736435000,\n 1674740035000,\n 1674743635000,\n 1674747235000\n ],\n [\n 5,\n null,\n 20,\n null,\n 40\n ]\n ],\n \"entities\": [null, { \"NaN\": [1]}]\n }\n }\n]", "refId": "A", @@ -989,7 +978,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1071,8 +1060,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"int64\",\n \"nullable\": true\n },\n \"config\": {\n \"thresholds\": {\n \"mode\": \"absolute\",\n \"steps\": [\n {\n \"color\": \"green\",\n \"value\": null\n }\n ]\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1674732835000,\n 1674736435000,\n 1674740035000,\n 1674743635000,\n 1674747235000,\n 1674750835000,\n 1674754235000,\n 1674757835000\n ],\n [\n null,\n null,\n false,\n true,\n true,\n false,\n true,\n null\n ]\n ],\n \"entities\": [null, { \"NaN\": [0], \"Undefined\": [1]}]\n }\n }\n]", "refId": "A", @@ -1086,12 +1074,7 @@ "preload": false, "refresh": "", "schemaVersion": 41, - "tags": [ - "gdev", - "panel-tests", - "state-timeline", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "state-timeline", "graph-ng"], "templating": { "list": [] }, @@ -1104,4 +1087,4 @@ "title": "StateTimeline - Thresholds & Mappings", "uid": "Kce7z9TVz", "version": 18 -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-bars-high-density.json b/devenv/dev-dashboards/panel-timeseries/timeseries-bars-high-density.json index 49e47870c71..158a1750a76 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-bars-high-density.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-bars-high-density.json @@ -1,600 +1,592 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 812, - "links": [], - "liveNow": false, - "panels": [ + "annotations": { + "list": [ { + "builtIn": 1, "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana", + "uid": "-- Grafana --" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "bars", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 1, - "maxDataPoints": 500, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "random_walk", - "seriesCount": 1 - } - ], - "title": "Dense (stroke only)", - "type": "timeseries" - }, - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "bars", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 4, - "maxDataPoints": 50, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "random_walk", - "seriesCount": 1 - } - ], - "title": "Normal (stroke only)", - "type": "timeseries" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "bars", - "fillOpacity": 60, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 0, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 10 - }, - "id": 2, - "maxDataPoints": 500, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 1, - "refId": "A" - } - ], - "title": "Dense (fill only)", - "type": "timeseries" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "bars", - "fillOpacity": 60, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 0, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 10 - }, - "id": 3, - "maxDataPoints": 50, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 4, - "refId": "A" - } - ], - "title": "Normal (fill only)", - "type": "timeseries" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "bars", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 20 - }, - "id": 6, - "maxDataPoints": 500, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 1, - "refId": "A" - } - ], - "title": "Dense (stroke + fill)", - "type": "timeseries" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "bars", - "fillOpacity": 20, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 20 - }, - "id": 5, - "maxDataPoints": 50, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 4, - "refId": "A" - } - ], - "title": "Normal (stroke + fill)", - "type": "timeseries" + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" } - ], - "refresh": "", - "schemaVersion": 39, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], - "templating": { - "list": [] + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 812, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "bars", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "maxDataPoints": 500, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1 + } + ], + "title": "Dense (stroke only)", + "type": "timeseries" }, - "time": { - "from": "now-6h", - "to": "now" + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "bars", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 4, + "maxDataPoints": 50, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1 + } + ], + "title": "Normal (stroke only)", + "type": "timeseries" }, - "timepicker": {}, - "timezone": "", - "title": "Panel Tests - TimeSeries - bars high density (stroke + fill)", - "uid": "c295a76e-79d3-43b3-a688-ef0373224158", - "version": 3, - "weekStart": "" - } \ No newline at end of file + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "bars", + "fillOpacity": 60, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 0, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 2, + "maxDataPoints": 500, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 1, + "refId": "A" + } + ], + "title": "Dense (fill only)", + "type": "timeseries" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "bars", + "fillOpacity": 60, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 0, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 3, + "maxDataPoints": 50, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 4, + "refId": "A" + } + ], + "title": "Normal (fill only)", + "type": "timeseries" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "bars", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 20 + }, + "id": 6, + "maxDataPoints": 500, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 1, + "refId": "A" + } + ], + "title": "Dense (stroke + fill)", + "type": "timeseries" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "bars", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 20 + }, + "id": 5, + "maxDataPoints": 50, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 4, + "refId": "A" + } + ], + "title": "Normal (stroke + fill)", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": ["gdev", "panel-tests", "graph-ng"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Panel Tests - TimeSeries - bars high density (stroke + fill)", + "uid": "c295a76e-79d3-43b3-a688-ef0373224158", + "version": 3, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-by-value-color-schemes.json b/devenv/dev-dashboards/panel-timeseries/timeseries-by-value-color-schemes.json index 11671725397..9271ecea7d9 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-by-value-color-schemes.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-by-value-color-schemes.json @@ -29,8 +29,7 @@ "panels": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -116,8 +115,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -129,8 +127,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -216,8 +213,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -229,8 +225,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -315,8 +310,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -328,8 +322,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -410,8 +403,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -521,8 +513,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -608,8 +599,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "max": 40, "min": 0, @@ -931,8 +921,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1021,8 +1010,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "max": 45, "min": 20, @@ -1034,8 +1022,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "max": 20, @@ -1052,11 +1039,7 @@ ], "refresh": "", "schemaVersion": 38, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-formats.json b/devenv/dev-dashboards/panel-timeseries/timeseries-formats.json index 9740e74a16b..bd42409a2f2 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-formats.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-formats.json @@ -79,9 +79,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "frameIndex": 0, @@ -103,128 +101,20 @@ "data": { "values": [ [ - 1677256641358, - 1677257007358, - 1677257373358, - 1677257739358, - 1677258105358, - 1677258471358, - 1677258837358, - 1677259203358, - 1677259569358, - 1677259935358, - 1677260301358, - 1677260667358, - 1677261033358, - 1677261399358, - 1677261765358, - 1677262131358, - 1677262497358, - 1677262863358, - 1677263229358, - 1677263595358, - 1677263961358, - 1677264327358, - 1677264693358, - 1677265059358, - 1677265425358, - 1677265791358, - 1677266157358, - 1677266523358, - 1677266889358, - 1677267255358, - 1677267621358, - 1677267987358, - 1677268353358, - 1677268719358, - 1677269085358, - 1677269451358, - 1677269817358, - 1677270183358, - 1677270549358, - 1677270915358, - 1677271281358, - 1677271647358, - 1677272013358, - 1677272379358, - 1677272745358, - 1677273111358, - 1677273477358, - 1677273843358, - 1677274209358, - 1677274575358, - 1677274941358, - 1677275307358, - 1677275673358, - 1677276039358, - 1677276405358, - 1677276771358, - 1677277137358, - 1677277503358, - 1677277869358, - 1677278235358 + 1677256641358, 1677257007358, 1677257373358, 1677257739358, 1677258105358, 1677258471358, + 1677258837358, 1677259203358, 1677259569358, 1677259935358, 1677260301358, 1677260667358, + 1677261033358, 1677261399358, 1677261765358, 1677262131358, 1677262497358, 1677262863358, + 1677263229358, 1677263595358, 1677263961358, 1677264327358, 1677264693358, 1677265059358, + 1677265425358, 1677265791358, 1677266157358, 1677266523358, 1677266889358, 1677267255358, + 1677267621358, 1677267987358, 1677268353358, 1677268719358, 1677269085358, 1677269451358, + 1677269817358, 1677270183358, 1677270549358, 1677270915358, 1677271281358, 1677271647358, + 1677272013358, 1677272379358, 1677272745358, 1677273111358, 1677273477358, 1677273843358, + 1677274209358, 1677274575358, 1677274941358, 1677275307358, 1677275673358, 1677276039358, + 1677276405358, 1677276771358, 1677277137358, 1677277503358, 1677277869358, 1677278235358 ], [ - 1, - 3, - 5, - 7, - 4, - 6, - 8, - 10, - 1, - 3, - 5, - 7, - 4, - 6, - 8, - 10, - 1, - 3, - 5, - 7, - 4, - 6, - 8, - 10, - 1, - 3, - 5, - 7, - 4, - 6, - 8, - 10, - 1, - 3, - 5, - 7, - 4, - 6, - 8, - 10, - 1, - 3, - 5, - 7, - 4, - 6, - 8, - 10, - 1, - 3, - 5, - 7, - 4, - 6, - 8, - 10, - 1, - 3, - 5, - 7 + 1, 3, 5, 7, 4, 6, 8, 10, 1, 3, 5, 7, 4, 6, 8, 10, 1, 3, 5, 7, 4, 6, 8, 10, 1, 3, 5, 7, 4, 6, 8, 10, + 1, 3, 5, 7, 4, 6, 8, 10, 1, 3, 5, 7, 4, 6, 8, 10, 1, 3, 5, 7, 4, 6, 8, 10, 1, 3, 5, 7 ], [ "a", @@ -394,10 +284,7 @@ ], "meta": { "type": "timeseries-long", - "typeVersion": [ - 0, - 0 - ] + "typeVersion": [0, 0] }, "name": "New Frame", "refId": "A" @@ -407,128 +294,20 @@ "data": { "values": [ [ - 1677256641358, - 1677257007358, - 1677257373358, - 1677257739358, - 1677258105358, - 1677258471358, - 1677258837358, - 1677259203358, - 1677259569358, - 1677259935358, - 1677260301358, - 1677260667358, - 1677261033358, - 1677261399358, - 1677261765358, - 1677262131358, - 1677262497358, - 1677262863358, - 1677263229358, - 1677263595358, - 1677263961358, - 1677264327358, - 1677264693358, - 1677265059358, - 1677265425358, - 1677265791358, - 1677266157358, - 1677266523358, - 1677266889358, - 1677267255358, - 1677267621358, - 1677267987358, - 1677268353358, - 1677268719358, - 1677269085358, - 1677269451358, - 1677269817358, - 1677270183358, - 1677270549358, - 1677270915358, - 1677271281358, - 1677271647358, - 1677272013358, - 1677272379358, - 1677272745358, - 1677273111358, - 1677273477358, - 1677273843358, - 1677274209358, - 1677274575358, - 1677274941358, - 1677275307358, - 1677275673358, - 1677276039358, - 1677276405358, - 1677276771358, - 1677277137358, - 1677277503358, - 1677277869358, - 1677278235358 + 1677256641358, 1677257007358, 1677257373358, 1677257739358, 1677258105358, 1677258471358, + 1677258837358, 1677259203358, 1677259569358, 1677259935358, 1677260301358, 1677260667358, + 1677261033358, 1677261399358, 1677261765358, 1677262131358, 1677262497358, 1677262863358, + 1677263229358, 1677263595358, 1677263961358, 1677264327358, 1677264693358, 1677265059358, + 1677265425358, 1677265791358, 1677266157358, 1677266523358, 1677266889358, 1677267255358, + 1677267621358, 1677267987358, 1677268353358, 1677268719358, 1677269085358, 1677269451358, + 1677269817358, 1677270183358, 1677270549358, 1677270915358, 1677271281358, 1677271647358, + 1677272013358, 1677272379358, 1677272745358, 1677273111358, 1677273477358, 1677273843358, + 1677274209358, 1677274575358, 1677274941358, 1677275307358, 1677275673358, 1677276039358, + 1677276405358, 1677276771358, 1677277137358, 1677277503358, 1677277869358, 1677278235358 ], [ - 1, - 2, - 3, - 2, - 3, - 5, - 6, - 3, - 1, - 2, - 3, - 2, - 3, - 5, - 6, - 3, - 1, - 2, - 3, - 2, - 3, - 5, - 6, - 3, - 1, - 2, - 3, - 2, - 3, - 5, - 6, - 3, - 1, - 2, - 3, - 2, - 3, - 5, - 6, - 3, - 1, - 2, - 3, - 2, - 3, - 5, - 6, - 3, - 1, - 2, - 3, - 2, - 3, - 5, - 6, - 3, - 1, - 2, - 3, - 2 + 1, 2, 3, 2, 3, 5, 6, 3, 1, 2, 3, 2, 3, 5, 6, 3, 1, 2, 3, 2, 3, 5, 6, 3, 1, 2, 3, 2, 3, 5, 6, 3, 1, + 2, 3, 2, 3, 5, 6, 3, 1, 2, 3, 2, 3, 5, 6, 3, 1, 2, 3, 2, 3, 5, 6, 3, 1, 2, 3, 2 ], [ "e", @@ -698,10 +477,7 @@ ], "meta": { "type": "timeseries-long", - "typeVersion": [ - 0, - 0 - ] + "typeVersion": [0, 0] }, "name": "New Frame", "refId": "B" @@ -803,7 +579,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 8, "w": 5, @@ -825,7 +603,9 @@ "type": "text" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -899,8 +679,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-nulls.json b/devenv/dev-dashboards/panel-timeseries/timeseries-nulls.json index a757845daa9..4e8da5c78df 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-nulls.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-nulls.json @@ -29,10 +29,7 @@ { "asDropdown": true, "icon": "external link", - "tags": [ - "gdev", - "graph-ng" - ], + "tags": ["gdev", "graph-ng"], "title": "Graph Tests", "type": "dashboards" } @@ -53,8 +50,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "description": "This visualizes a time series of enumerated values, displaying minimum, maximum, and average values over time to track fluctuations and trends", "fieldConfig": { @@ -167,10 +163,10 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, - "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"name\": \"A\",\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Min\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Max\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Status\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1686730601000,\n 1686730610000,\n 1686730615000,\n 1686730635000,\n 1686730666000,\n 1686730679000,\n 1686730700000,\n 1686730705000\n ],\n [\n 97.56700088585262,\n 97.51875596242603,\n 97.88367836664669,\n 97.64813860191066,\n 97.46344237059202,\n 97.06932713353162,\n 97.11185691118344,\n 97.4315327939052\n ],\n [\n 96.17381933561288,\n 96.86449303671965,\n 95.5851820937877,\n 96.94658694041154,\n 96.7433590164638,\n 96.74913605945137,\n 95.61938577725707,\n 96.24186355022567\n ],\n [\n 99.69898997339244,\n 98.68620205532211,\n 99.5800005905314,\n 98.2998754185445,\n 98.02873998081307,\n 99.01798295660049,\n 97.95260164072228,\n 98.24811606848421\n ],\n [\n \"ON\",\n \"ON\",\n \"OFF\",\n \"ON\",\n \"OFF\",\n \"ON\",\n \"STANDBY\",\n \"OFF\"\n ]\n ]\n }\n }\n]", "refId": "A", + "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"name\": \"A\",\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Min\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Max\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Status\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1686730601000,\n 1686730610000,\n 1686730615000,\n 1686730635000,\n 1686730666000,\n 1686730679000,\n 1686730700000,\n 1686730705000\n ],\n [\n 97.56700088585262,\n 97.51875596242603,\n 97.88367836664669,\n 97.64813860191066,\n 97.46344237059202,\n 97.06932713353162,\n 97.11185691118344,\n 97.4315327939052\n ],\n [\n 96.17381933561288,\n 96.86449303671965,\n 95.5851820937877,\n 96.94658694041154,\n 96.7433590164638,\n 96.74913605945137,\n 95.61938577725707,\n 96.24186355022567\n ],\n [\n 99.69898997339244,\n 98.68620205532211,\n 99.5800005905314,\n 98.2998754185445,\n 98.02873998081307,\n 99.01798295660049,\n 97.95260164072228,\n 98.24811606848421\n ],\n [\n \"ON\",\n \"ON\",\n \"OFF\",\n \"ON\",\n \"OFF\",\n \"ON\",\n \"STANDBY\",\n \"OFF\"\n ]\n ]\n }\n }\n]", + "refId": "A", "scenarioId": "raw_frame" } ], @@ -183,11 +179,7 @@ { "destinationType": "enum", "enumConfig": { - "text": [ - "OFF", - "STANDBY", - "ON" - ] + "text": ["OFF", "STANDBY", "ON"] }, "targetField": "Status" } @@ -200,8 +192,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -288,8 +279,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"name\": \"A\",\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Min\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Max\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Status\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1686730601000,\n 1686730610000,\n 1686730615000,\n 1686730635000,\n 1686730666000,\n 1686730679000,\n 1686730700000,\n 1686730705000\n ],\n [\n 97.56700088585262,\n 97.51875596242603,\n 97.88367836664669,\n 97.64813860191066,\n 97.46344237059202,\n 97.06932713353162,\n 97.11185691118344,\n 97.4315327939052\n ],\n [\n 96.17381933561288,\n 96.86449303671965,\n 95.5851820937877,\n 96.94658694041154,\n 96.7433590164638,\n 96.74913605945137,\n 95.61938577725707,\n 96.24186355022567\n ],\n [\n 99.69898997339244,\n 98.68620205532211,\n 99.5800005905314,\n 98.2998754185445,\n 98.02873998081307,\n 99.01798295660049,\n 97.95260164072228,\n 98.24811606848421\n ],\n [\n \"ON\",\n \"ON\",\n \"OFF\",\n \"ON\",\n \"OFF\",\n \"ON\",\n \"STANDBY\",\n \"OFF\"\n ]\n ]\n }\n }\n]", "refId": "A", @@ -305,11 +295,7 @@ { "destinationType": "enum", "enumConfig": { - "text": [ - "OFF", - "STANDBY", - "ON" - ] + "text": ["OFF", "STANDBY", "ON"] }, "targetField": "Status" } @@ -335,7 +321,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -447,7 +433,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "description": "Series A have no nulls and is not aligned with series B", "fieldConfig": { @@ -569,7 +555,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -711,7 +697,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -823,7 +809,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "description": "Series A have no nulls and is not aligned with series B", "fieldConfig": { @@ -945,7 +931,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "description": "Should look the same as above\n", "fieldConfig": { @@ -1087,7 +1073,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1235,7 +1221,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1383,7 +1369,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1474,7 +1460,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1556,8 +1542,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1569,7 +1554,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1649,8 +1634,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1662,7 +1646,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1742,8 +1726,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1755,7 +1738,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1835,8 +1818,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1848,7 +1830,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1928,8 +1910,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1941,7 +1922,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2021,8 +2002,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -2034,7 +2014,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2114,8 +2094,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -2127,7 +2106,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2207,8 +2186,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -2220,7 +2198,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2300,8 +2278,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -2326,8 +2303,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2409,8 +2385,7 @@ "alias": "", "csvContent": "time, val\n2023-06-14T08:16:41Z, 20\n2023-06-14T08:16:42Z, 22\n2023-06-14T08:16:43Z, 24\n2023-06-14T08:16:45Z, 10\n2023-06-14T08:16:46Z, 12\n2023-06-14T08:17:46Z, 13\n2023-06-14T08:17:47Z, 14\n2023-06-14T08:17:57Z, 20\n2023-06-14T08:17:58Z, null\n2023-06-14T08:18:00Z, 22\n2023-06-14T08:18:25Z, 22", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -2421,7 +2396,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2503,8 +2478,7 @@ "alias": "", "csvContent": "x, val\n1, 20\n2, 22\n3, 24\n5, 10\n6, 12\n66, 13\n67, 14\n77, 20\n78, null\n80, 22\n105, 22", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -2515,7 +2489,7 @@ }, { "datasource": { - "type": "testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -2592,8 +2566,7 @@ "alias": "", "csvContent": "time, val\n2023-06-14T08:16:41Z, 20\n2023-06-14T08:16:42Z, 22\n2023-06-14T08:16:43Z, 24\n2023-06-14T08:16:45Z, 10\n2023-06-14T08:16:46Z, 12\n2023-06-14T08:17:46Z, 13\n2023-06-14T08:17:47Z, 14\n2023-06-14T08:17:57Z, 20\n2023-06-14T08:17:58Z, null\n2023-06-14T08:18:00Z, 22\n2023-06-14T08:18:25Z, 22", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -3428,15 +3401,7 @@ "preload": false, "refresh": false, "schemaVersion": 41, - "tags": [ - "gdev", - "panel-tests", - "graph-ng", - "timeseries", - "trend", - "state-timeline", - "transform" - ], + "tags": ["gdev", "panel-tests", "graph-ng", "timeseries", "trend", "state-timeline", "transform"], "templating": { "list": [] }, @@ -3449,4 +3414,4 @@ "title": "Panel Tests - Graph NG - Discrete panels", "uid": "8mmCAF1Mz", "version": 1 -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-out-of-rage.json b/devenv/dev-dashboards/panel-timeseries/timeseries-out-of-rage.json index 383085604f0..b368963cc47 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-out-of-rage.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-out-of-rage.json @@ -1,512 +1,498 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 1435, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "blue", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 10, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 8, - "x": 0, - "y": 0 - }, - "id": 3, - "maxDataPoints": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.4.0-pre", - "targets": [ - { - "csvContent": "Time,Value,Name\n2022-09-01T05:00:00Z,100,Before\n2022-09-01T06:00:00Z,100,Middle", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "B", - "scenarioId": "csv_content" - } - ], - "title": "Before + Middle", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "blue", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 10, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 8, - "x": 8, - "y": 0 - }, - "id": 2, - "maxDataPoints": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.4.0-pre", - "targets": [ - { - "csvContent": "Time,Value,Name\n2022-09-01T05:00:00Z,100,Before\n2022-09-01T07:00:00Z,100,After\n", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "B", - "scenarioId": "csv_content" - } - ], - "title": "Before + After", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "blue", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 10, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 8, - "x": 16, - "y": 0 - }, - "id": 4, - "maxDataPoints": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.4.0-pre", - "targets": [ - { - "csvContent": "Time,Value,Name\n2022-09-01T06:00:00Z,100,Middle\n2022-09-01T07:00:00Z,100,After\n", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "B", - "scenarioId": "csv_content" - } - ], - "title": "Middle + After", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "blue", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 10, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 9 - }, - "id": 5, - "maxDataPoints": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.4.0-pre", - "targets": [ - { - "csvContent": "Time,Value,Name\n2022-09-01T04:00:00Z,100,Before1\n2022-09-01T05:00:00Z,100,Before2\n", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "B", - "scenarioId": "csv_content" - } - ], - "title": "Two points before (show zoom button)", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "blue", - "mode": "fixed" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 10, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 9 - }, - "id": 6, - "maxDataPoints": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "9.4.0-pre", - "targets": [ - { - "csvContent": "Time,Value,Name\n2022-09-01T07:00:00Z,100,After1\n2022-09-01T08:00:00Z,100,After2\n", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "B", - "scenarioId": "csv_content" - } - ], - "title": "Two points after (show zoom button)", - "type": "timeseries" + "type": "dashboard" } - ], - "refresh": false, - "schemaVersion": 37, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], - "templating": { - "list": [] + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1435, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "fixed" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 0 + }, + "id": 3, + "maxDataPoints": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "csvContent": "Time,Value,Name\n2022-09-01T05:00:00Z,100,Before\n2022-09-01T06:00:00Z,100,Middle", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "B", + "scenarioId": "csv_content" + } + ], + "title": "Before + Middle", + "type": "timeseries" }, - "time": { - "from": "now-6h", - "to": "now" + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "fixed" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 0 + }, + "id": 2, + "maxDataPoints": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "csvContent": "Time,Value,Name\n2022-09-01T05:00:00Z,100,Before\n2022-09-01T07:00:00Z,100,After\n", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "B", + "scenarioId": "csv_content" + } + ], + "title": "Before + After", + "type": "timeseries" }, - "timepicker": {}, - "timezone": "", - "title": "Panel Tests - Timeseries - Out of range", - "uid": "pqnrfd4Vz", - "version": 1, - "weekStart": "" - } + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "fixed" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 0 + }, + "id": 4, + "maxDataPoints": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "csvContent": "Time,Value,Name\n2022-09-01T06:00:00Z,100,Middle\n2022-09-01T07:00:00Z,100,After\n", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "B", + "scenarioId": "csv_content" + } + ], + "title": "Middle + After", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "fixed" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 5, + "maxDataPoints": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "csvContent": "Time,Value,Name\n2022-09-01T04:00:00Z,100,Before1\n2022-09-01T05:00:00Z,100,Before2\n", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "B", + "scenarioId": "csv_content" + } + ], + "title": "Two points before (show zoom button)", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "fixed" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 6, + "maxDataPoints": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "csvContent": "Time,Value,Name\n2022-09-01T07:00:00Z,100,After1\n2022-09-01T08:00:00Z,100,After2\n", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "B", + "scenarioId": "csv_content" + } + ], + "title": "Two points after (show zoom button)", + "type": "timeseries" + } + ], + "refresh": false, + "schemaVersion": 37, + "tags": ["gdev", "panel-tests", "graph-ng"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Panel Tests - Timeseries - Out of range", + "uid": "pqnrfd4Vz", + "version": 1, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-shared-tooltip-cursor-position.json b/devenv/dev-dashboards/panel-timeseries/timeseries-shared-tooltip-cursor-position.json index f7fb3dc6455..74b3f2bae16 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-shared-tooltip-cursor-position.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-shared-tooltip-cursor-position.json @@ -540,11 +540,7 @@ ], "refresh": false, "schemaVersion": 28, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-stacking.json b/devenv/dev-dashboards/panel-timeseries/timeseries-stacking.json index ed99cd6c08c..2b4f64dce05 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-stacking.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-stacking.json @@ -879,11 +879,7 @@ } ], "schemaVersion": 27, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-stacking2.json b/devenv/dev-dashboards/panel-timeseries/timeseries-stacking2.json index 18588cd8830..64c4a21d1ad 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-stacking2.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-stacking2.json @@ -28,7 +28,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "", "fieldConfig": { "defaults": { @@ -101,8 +103,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -110,8 +111,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_metric_values", @@ -119,8 +119,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "csv_metric_values", @@ -305,7 +304,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "", "fieldConfig": { "defaults": { @@ -379,8 +380,7 @@ { "alias": "+100", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "A", @@ -390,8 +390,7 @@ { "alias": "+50", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_metric_values", @@ -400,8 +399,7 @@ { "alias": "+25", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "csv_metric_values", @@ -410,8 +408,7 @@ { "alias": "-50", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "csv_metric_values", @@ -420,8 +417,7 @@ { "alias": "-100", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "E", "scenarioId": "csv_metric_values", @@ -606,7 +602,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "", "fieldConfig": { "defaults": { @@ -693,8 +691,7 @@ { "alias": "NegY", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "csv_metric_values", @@ -702,8 +699,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -711,8 +707,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_metric_values", @@ -721,8 +716,7 @@ { "alias": "Neg", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "csv_metric_values", @@ -933,7 +927,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "", "fieldConfig": { "defaults": { @@ -984,9 +980,7 @@ "barWidth": 1, "groupWidth": 0.89, "legend": { - "calcs": [ - "max" - ], + "calcs": ["max"], "displayMode": "hidden", "placement": "right" }, @@ -1005,8 +999,7 @@ { "csvContent": "Name,Stat1,Stat2,Stat3,Stat4,Stat5,Stat6,Stat7,Stat8\nA, 10, 15,8,3,4,12,14,1\nB, 19, 5,8,3,4,12,14,6\nC, 15, 5,8,3,4,10,4,6\n", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -1070,9 +1063,7 @@ "barWidth": 1, "groupWidth": 0.89, "legend": { - "calcs": [ - "max" - ], + "calcs": ["max"], "displayMode": "hidden", "placement": "right" }, @@ -1155,9 +1146,7 @@ "barWidth": 1, "groupWidth": 0.89, "legend": { - "calcs": [ - "max" - ], + "calcs": ["max"], "displayMode": "hidden", "placement": "right" }, @@ -1486,7 +1475,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1579,8 +1570,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1588,8 +1578,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_metric_values", @@ -1597,8 +1586,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "csv_metric_values", @@ -1606,8 +1594,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "csv_metric_values", @@ -1833,7 +1820,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "", "fieldConfig": { "defaults": { @@ -1907,8 +1896,7 @@ { "alias": "+100", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "A", @@ -1918,8 +1906,7 @@ { "alias": "+50", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_metric_values", @@ -1928,8 +1915,7 @@ { "alias": "+25", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "csv_metric_values", @@ -1938,8 +1924,7 @@ { "alias": "-50", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "csv_metric_values", @@ -1948,8 +1933,7 @@ { "alias": "-100", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "E", "scenarioId": "csv_metric_values", @@ -2047,7 +2031,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "", "fieldConfig": { "defaults": { @@ -2133,8 +2119,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "A", @@ -2144,8 +2129,7 @@ { "alias": "NegY", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_metric_values", @@ -2156,7 +2140,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2228,8 +2214,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -2237,8 +2222,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_metric_values", @@ -2246,8 +2230,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "csv_metric_values", @@ -2255,8 +2238,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "csv_metric_values", @@ -2474,7 +2456,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2584,8 +2568,7 @@ { "csvContent": "\"Time\",\"Sum daily.cost_per_kwh\"\n1639612800000,\n1639872000000,0\n1639958400000,272\n1640044800000,272\n1640131200000,327\n1640217600000,290\n1640304000000,286\n1640390400000,256\n1640476800000,397\n1640563200000,295\n1640649600000,310\n1640736000000,224\n1640822400000,291\n1640908800000,265\n1640995200000,256\n1641081600000,399\n1641168000000,471\n1641254400000,325\n1641340800000,363\n1641427200000,375\n1641513600000,365\n1641600000000,492\n1641686400000,409\n1641772800000,371\n1641859200000,448\n1641945600000,372\n1642032000000,0\n1642118400000,0\n1642204800000,0\n1642291200000,0\n1642377600000,0\n1642464000000,0\n\n", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "A", @@ -2594,8 +2577,7 @@ { "csvContent": "\"Time\",\"Sum morning.cost_per_kwh\",\"Sum afternoon.cost_per_kwh\",\"Sum evening.cost_per_kwh\",\"Sum night.cost_per_kwh\",\"Sum late.cost_per_kwh\"\n1639612800000,0,0,0,0,0\n1639872000000,,,,,\n1639958400000,,,,,\n1640044800000,,,,,\n1640131200000,,,,,\n1640217600000,32,28,62,63,32\n1640304000000,,,,,\n1640390400000,,,,,\n1640476800000,,,,,\n1640563200000,,,,,\n1640649600000,,,,,\n1640736000000,,,,,\n1640822400000,47,53,68,96,45\n1640908800000,,,,,\n1640995200000,,,,,\n1641081600000,,,,,\n1641168000000,,,,,\n1641254400000,,,,,\n1641340800000,,,,,\n1641427200000,112,130,147,152,115\n1641513600000,,,,,\n1641600000000,,,,,\n1641686400000,,,,,\n1641772800000,,,,,\n1641859200000,,,,,\n1641945600000,,,,,\n1642032000000,0,0,0,0,0\n1642118400000,,,,,\n1642204800000,,,,,\n1642291200000,,,,,\n1642377600000,,,,,\n1642464000000,,,,,\n\n", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "B", @@ -2607,7 +2589,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2717,8 +2701,7 @@ { "csvContent": "\"Time\",\"Sum daily.cost_per_kwh\"\n1639612800000,\n1639872000000,0\n1639958400000,272\n1640044800000,272\n1640131200000,327\n1640217600000,290\n1640304000000,286\n1640390400000,256\n1640476800000,397\n1640563200000,295\n1640649600000,310\n1640736000000,224\n1640822400000,291\n1640908800000,265\n1640995200000,256\n1641081600000,399\n1641168000000,471\n1641254400000,325\n1641340800000,363\n1641427200000,375\n1641513600000,365\n1641600000000,492\n1641686400000,409\n1641772800000,371\n1641859200000,448\n1641945600000,372\n1642032000000,0\n1642118400000,0\n1642204800000,0\n1642291200000,0\n1642377600000,0\n1642464000000,0\n\n", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "A", @@ -2727,8 +2710,7 @@ { "csvContent": "\"Time\",\"Sum morning.cost_per_kwh\",\"Sum afternoon.cost_per_kwh\",\"Sum evening.cost_per_kwh\",\"Sum night.cost_per_kwh\",\"Sum late.cost_per_kwh\"\n1639612800000,0,0,0,0,0\n1639872000000,,,,,\n1639958400000,,,,,\n1640044800000,,,,,\n1640131200000,,,,,\n1640217600000,32,28,62,63,32\n1640304000000,,,,,\n1640390400000,,,,,\n1640476800000,,,,,\n1640563200000,,,,,\n1640649600000,,,,,\n1640736000000,,,,,\n1640822400000,47,53,68,96,45\n1640908800000,,,,,\n1640995200000,,,,,\n1641081600000,,,,,\n1641168000000,,,,,\n1641254400000,,,,,\n1641340800000,,,,,\n1641427200000,112,130,147,152,115\n1641513600000,,,,,\n1641600000000,,,,,\n1641686400000,,,,,\n1641772800000,,,,,\n1641859200000,,,,,\n1641945600000,,,,,\n1642032000000,0,0,0,0,0\n1642118400000,,,,,\n1642204800000,,,,,\n1642291200000,,,,,\n1642377600000,,,,,\n1642464000000,,,,,\n\n", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "B", @@ -2740,7 +2722,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2850,8 +2834,7 @@ { "csvContent": "\"Time\",\"Sum daily.cost_per_kwh\"\n1639612800000,\n1639872000000,0\n1639958400000,272\n1640044800000,272\n1640131200000,327\n1640217600000,290\n1640304000000,286\n1640390400000,256\n1640476800000,397\n1640563200000,295\n1640649600000,310\n1640736000000,224\n1640822400000,291\n1640908800000,265\n1640995200000,256\n1641081600000,399\n1641168000000,471\n1641254400000,325\n1641340800000,363\n1641427200000,375\n1641513600000,365\n1641600000000,492\n1641686400000,409\n1641772800000,371\n1641859200000,448\n1641945600000,372\n1642032000000,0\n1642118400000,0\n1642204800000,0\n1642291200000,0\n1642377600000,0\n1642464000000,0\n\n", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "A", @@ -2860,8 +2843,7 @@ { "csvContent": "\"Time\",\"Sum morning.cost_per_kwh\",\"Sum afternoon.cost_per_kwh\",\"Sum evening.cost_per_kwh\",\"Sum night.cost_per_kwh\",\"Sum late.cost_per_kwh\"\n1639612800000,0,0,0,0,0\n1639872000000,,,,,\n1639958400000,,,,,\n1640044800000,,,,,\n1640131200000,,,,,\n1640217600000,32,28,62,63,32\n1640304000000,,,,,\n1640390400000,,,,,\n1640476800000,,,,,\n1640563200000,,,,,\n1640649600000,,,,,\n1640736000000,,,,,\n1640822400000,47,53,68,96,45\n1640908800000,,,,,\n1640995200000,,,,,\n1641081600000,,,,,\n1641168000000,,,,,\n1641254400000,,,,,\n1641340800000,,,,,\n1641427200000,112,130,147,152,115\n1641513600000,,,,,\n1641600000000,,,,,\n1641686400000,,,,,\n1641772800000,,,,,\n1641859200000,,,,,\n1641945600000,,,,,\n1642032000000,0,0,0,0,0\n1642118400000,,,,,\n1642204800000,,,,,\n1642291200000,,,,,\n1642377600000,,,,,\n1642464000000,,,,,\n\n", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "B", @@ -2873,7 +2855,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2945,8 +2929,7 @@ { "csvContent": "time,Label 1\n2021-12-20T05:09:05.832Z,1000", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -2954,8 +2937,7 @@ { "csvContent": "time,Label 10\n2021-12-18T04:54:45.888Z,2500\n2021-12-20T05:09:05.832Z,600\n2021-12-22T05:23:25.776Z,350\n2022-01-03T06:49:25.440Z,500", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_content" @@ -2963,8 +2945,7 @@ { "csvContent": "time,Label 11\n2021-12-18T04:54:45.888Z,28000\n2021-12-20T05:09:05.832Z,3100\n2021-12-22T05:23:25.776Z,36000\n2021-12-24T05:37:45.720Z,2800", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "csv_content" @@ -2972,8 +2953,7 @@ { "csvContent": "time,Label 12\n2021-12-20T05:09:05.832Z,255\n2021-12-24T05:37:45.720Z,651\n2021-12-26T05:52:05.664Z,50", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "csv_content" @@ -2981,8 +2961,7 @@ { "csvContent": "time,Label 13\n2021-12-18T04:54:45.888Z,5000\n2021-12-20T05:09:05.832Z,1231", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "E", "scenarioId": "csv_content" @@ -2990,8 +2969,7 @@ { "csvContent": "time,Label 14\n2021-12-14T04:26:06.000Z,122\n2021-12-16T04:40:25.944Z,123\n2021-12-18T04:54:45.888Z,12345\n2021-12-20T05:09:05.832Z,23456\n2021-12-22T05:23:25.776Z,34567\n2021-12-24T05:37:45.720Z,12345\n2021-12-26T05:52:05.664Z,8000\n2021-12-28T06:06:25.608Z,3000\n2021-12-30T06:20:45.552Z,1000\n2022-01-01T06:35:05.496Z,21", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "csv_content" @@ -2999,8 +2977,7 @@ { "csvContent": "time,Label 15\n2022-01-07T07:18:05.328Z,20", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "G", "scenarioId": "csv_content" @@ -3008,8 +2985,7 @@ { "csvContent": "time,Label 16\n2022-01-03T06:49:25.440Z,210\n2022-01-07T07:18:05.328Z,321", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "H", "scenarioId": "csv_content" @@ -3017,8 +2993,7 @@ { "csvContent": "time,Label 17\n2021-12-28T06:06:25.608Z,210\n2022-01-07T07:18:05.328Z,210", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "I", "scenarioId": "csv_content" @@ -3026,8 +3001,7 @@ { "csvContent": "time,Label 18\n2021-12-18T04:54:45.888Z,250\n2021-12-20T05:09:05.832Z,852\n2021-12-22T05:23:25.776Z,1234\n2021-12-26T05:52:05.664Z,321\n2021-12-30T06:20:45.552Z,432", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "J", "scenarioId": "csv_content" @@ -3035,8 +3009,7 @@ { "csvContent": "time,Label 2\n2021-12-24T05:37:45.720Z,543\n2021-12-26T05:52:05.664Z,18000\n2021-12-28T06:06:25.608Z,17000\n2021-12-30T06:20:45.552Z,12000\n2022-01-01T06:35:05.496Z,8500\n2022-01-03T06:49:25.440Z,8000\n2022-01-05T07:03:45.384Z,5000\n2022-01-07T07:18:05.328Z,3000\n2022-01-09T07:32:25.272Z,2500\n2022-01-11T07:46:45.216Z,2200\n2022-01-13T08:01:05.160Z,3000\n2022-01-15T08:15:25.104Z,1520\n2022-01-17T08:29:45.048Z,665.35", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "K", "scenarioId": "csv_content" @@ -3044,8 +3017,7 @@ { "csvContent": "time,Label 3\n2022-01-11T07:46:45.216Z,800", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "L", "scenarioId": "csv_content" @@ -3053,8 +3025,7 @@ { "csvContent": "time,Label 4\n2021-12-22T05:23:25.776Z,14173\n2021-12-24T05:37:45.720Z,14805\n2021-12-26T05:52:05.664Z,5600\n2021-12-28T06:06:25.608Z,5950\n2021-12-30T06:20:45.552Z,775\n2022-01-01T06:35:05.496Z,725\n2022-01-03T06:49:25.440Z,1450\n2022-01-05T07:03:45.384Z,3175\n2022-01-07T07:18:05.328Z,1850\n2022-01-09T07:32:25.272Z,1025\n2022-01-11T07:46:45.216Z,2700\n2022-01-13T08:01:05.160Z,4825\n2022-01-15T08:15:25.104Z,3600", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "M", "scenarioId": "csv_content" @@ -3062,8 +3033,7 @@ { "csvContent": "time,Label 5\n2022-01-15T08:15:25.104Z,1675", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "N", "scenarioId": "csv_content" @@ -3071,8 +3041,7 @@ { "csvContent": "time,Label 6\n2021-12-22T05:23:25.776Z,433.16", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "O", "scenarioId": "csv_content" @@ -3080,8 +3049,7 @@ { "csvContent": "time,Label 7\n2021-12-24T05:37:45.720Z,41250\n2021-12-26T05:52:05.664Z,45150\n2021-12-28T06:06:25.608Z,45870.16\n2021-12-30T06:20:45.552Z,38728.17\n2022-01-01T06:35:05.496Z,39931.77\n2022-01-03T06:49:25.440Z,39831.8\n2022-01-05T07:03:45.384Z,38252.06\n2022-01-07T07:18:05.328Z,44332.92\n2022-01-09T07:32:25.272Z,51359.74\n2022-01-11T07:46:45.216Z,56155.84\n2022-01-13T08:01:05.160Z,55676.92\n2022-01-15T08:15:25.104Z,55323.84\n2022-01-17T08:29:45.048Z,13830.96", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "P", "scenarioId": "csv_content" @@ -3089,8 +3057,7 @@ { "csvContent": "time,Label 8\n2021-12-30T06:20:45.552Z,52.89\n2022-01-01T06:35:05.496Z,569.57", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "Q", "scenarioId": "csv_content" @@ -3098,8 +3065,7 @@ { "csvContent": "time,Label 9\n2022-01-01T06:35:05.496Z,2140.34\n2022-01-03T06:49:25.440Z,4074.92\n2022-01-05T07:03:45.384Z,1557.85\n2022-01-07T07:18:05.328Z,1097.74\n2022-01-09T07:32:25.272Z,692.06\n2022-01-11T07:46:45.216Z,758.67\n2022-01-13T08:01:05.160Z,957.56\n2022-01-15T08:15:25.104Z,1470.49\n2022-01-17T08:29:45.048Z,198.18", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "R", "scenarioId": "csv_content" @@ -3109,7 +3075,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -3181,8 +3149,7 @@ { "csvContent": "time,Label 1\n2021-12-20T05:09:05.832Z,1000", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -3190,8 +3157,7 @@ { "csvContent": "time,Label 10\n2021-12-18T04:54:45.888Z,2500\n2021-12-20T05:09:05.832Z,600\n2021-12-22T05:23:25.776Z,350\n2022-01-03T06:49:25.440Z,500", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_content" @@ -3199,8 +3165,7 @@ { "csvContent": "time,Label 11\n2021-12-18T04:54:45.888Z,28000\n2021-12-20T05:09:05.832Z,3100\n2021-12-22T05:23:25.776Z,36000\n2021-12-24T05:37:45.720Z,2800", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "csv_content" @@ -3208,8 +3173,7 @@ { "csvContent": "time,Label 12\n2021-12-20T05:09:05.832Z,255\n2021-12-24T05:37:45.720Z,651\n2021-12-26T05:52:05.664Z,50", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "csv_content" @@ -3217,8 +3181,7 @@ { "csvContent": "time,Label 13\n2021-12-18T04:54:45.888Z,5000\n2021-12-20T05:09:05.832Z,1231", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "E", "scenarioId": "csv_content" @@ -3226,8 +3189,7 @@ { "csvContent": "time,Label 14\n2021-12-14T04:26:06.000Z,122\n2021-12-16T04:40:25.944Z,123\n2021-12-18T04:54:45.888Z,12345\n2021-12-20T05:09:05.832Z,23456\n2021-12-22T05:23:25.776Z,34567\n2021-12-24T05:37:45.720Z,12345\n2021-12-26T05:52:05.664Z,8000\n2021-12-28T06:06:25.608Z,3000\n2021-12-30T06:20:45.552Z,1000\n2022-01-01T06:35:05.496Z,21", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "F", "scenarioId": "csv_content" @@ -3235,8 +3197,7 @@ { "csvContent": "time,Label 15\n2022-01-07T07:18:05.328Z,20", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "G", "scenarioId": "csv_content" @@ -3244,8 +3205,7 @@ { "csvContent": "time,Label 16\n2022-01-03T06:49:25.440Z,210\n2022-01-07T07:18:05.328Z,321", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "H", "scenarioId": "csv_content" @@ -3253,8 +3213,7 @@ { "csvContent": "time,Label 17\n2021-12-28T06:06:25.608Z,210\n2022-01-07T07:18:05.328Z,210", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "I", "scenarioId": "csv_content" @@ -3262,8 +3221,7 @@ { "csvContent": "time,Label 18\n2021-12-18T04:54:45.888Z,250\n2021-12-20T05:09:05.832Z,852\n2021-12-22T05:23:25.776Z,1234\n2021-12-26T05:52:05.664Z,321\n2021-12-30T06:20:45.552Z,432", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "J", "scenarioId": "csv_content" @@ -3271,8 +3229,7 @@ { "csvContent": "time,Label 2\n2021-12-24T05:37:45.720Z,543\n2021-12-26T05:52:05.664Z,18000\n2021-12-28T06:06:25.608Z,17000\n2021-12-30T06:20:45.552Z,12000\n2022-01-01T06:35:05.496Z,8500\n2022-01-03T06:49:25.440Z,8000\n2022-01-05T07:03:45.384Z,5000\n2022-01-07T07:18:05.328Z,3000\n2022-01-09T07:32:25.272Z,2500\n2022-01-11T07:46:45.216Z,2200\n2022-01-13T08:01:05.160Z,3000\n2022-01-15T08:15:25.104Z,1520\n2022-01-17T08:29:45.048Z,665.35", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "K", "scenarioId": "csv_content" @@ -3280,8 +3237,7 @@ { "csvContent": "time,Label 3\n2022-01-11T07:46:45.216Z,800", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "L", "scenarioId": "csv_content" @@ -3289,8 +3245,7 @@ { "csvContent": "time,Label 4\n2021-12-22T05:23:25.776Z,14173\n2021-12-24T05:37:45.720Z,14805\n2021-12-26T05:52:05.664Z,5600\n2021-12-28T06:06:25.608Z,5950\n2021-12-30T06:20:45.552Z,775\n2022-01-01T06:35:05.496Z,725\n2022-01-03T06:49:25.440Z,1450\n2022-01-05T07:03:45.384Z,3175\n2022-01-07T07:18:05.328Z,1850\n2022-01-09T07:32:25.272Z,1025\n2022-01-11T07:46:45.216Z,2700\n2022-01-13T08:01:05.160Z,4825\n2022-01-15T08:15:25.104Z,3600", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "M", "scenarioId": "csv_content" @@ -3298,8 +3253,7 @@ { "csvContent": "time,Label 5\n2022-01-15T08:15:25.104Z,1675", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "N", "scenarioId": "csv_content" @@ -3307,8 +3261,7 @@ { "csvContent": "time,Label 6\n2021-12-22T05:23:25.776Z,433.16", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "O", "scenarioId": "csv_content" @@ -3316,8 +3269,7 @@ { "csvContent": "time,Label 7\n2021-12-24T05:37:45.720Z,41250\n2021-12-26T05:52:05.664Z,45150\n2021-12-28T06:06:25.608Z,45870.16\n2021-12-30T06:20:45.552Z,38728.17\n2022-01-01T06:35:05.496Z,39931.77\n2022-01-03T06:49:25.440Z,39831.8\n2022-01-05T07:03:45.384Z,38252.06\n2022-01-07T07:18:05.328Z,44332.92\n2022-01-09T07:32:25.272Z,51359.74\n2022-01-11T07:46:45.216Z,56155.84\n2022-01-13T08:01:05.160Z,55676.92\n2022-01-15T08:15:25.104Z,55323.84\n2022-01-17T08:29:45.048Z,13830.96", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "P", "scenarioId": "csv_content" @@ -3325,8 +3277,7 @@ { "csvContent": "time,Label 8\n2021-12-30T06:20:45.552Z,52.89\n2022-01-01T06:35:05.496Z,569.57", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "Q", "scenarioId": "csv_content" @@ -3334,8 +3285,7 @@ { "csvContent": "time,Label 9\n2022-01-01T06:35:05.496Z,2140.34\n2022-01-03T06:49:25.440Z,4074.92\n2022-01-05T07:03:45.384Z,1557.85\n2022-01-07T07:18:05.328Z,1097.74\n2022-01-09T07:32:25.272Z,692.06\n2022-01-11T07:46:45.216Z,758.67\n2022-01-13T08:01:05.160Z,957.56\n2022-01-15T08:15:25.104Z,1470.49\n2022-01-17T08:29:45.048Z,198.18", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "R", "scenarioId": "csv_content" @@ -3345,7 +3295,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -3414,8 +3366,7 @@ { "csvContent": "name, number, number2, number3, number4\nName1, 40, 5, 20, 10\nName2, 0,0,0,0\nName3, 6, 3, 5, 1\nName4, 1, 1, 1, 2", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -3427,11 +3378,7 @@ ], "refresh": false, "schemaVersion": 36, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-thresholds.json b/devenv/dev-dashboards/panel-timeseries/timeseries-thresholds.json index da583b7d232..4275255778f 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-thresholds.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-thresholds.json @@ -1228,11 +1228,7 @@ } ], "schemaVersion": 29, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-y-ticks-zero-decimals.json b/devenv/dev-dashboards/panel-timeseries/timeseries-y-ticks-zero-decimals.json index 1c4e7466b64..cd41cd73e26 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-y-ticks-zero-decimals.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-y-ticks-zero-decimals.json @@ -1,845 +1,828 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 339, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 32, - "w": 4, - "x": 0, - "y": 0 - }, - "id": 2, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_metric_values", - "stringInput": "1,2.3" - } - ], - "title": "Panel Title", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 0, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 32, - "w": 4, - "x": 4, - "y": 0 - }, - "id": 5, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_metric_values", - "stringInput": "1,2.3" - } - ], - "title": "Panel Title", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 17, - "w": 4, - "x": 8, - "y": 0 - }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_metric_values", - "stringInput": "1,2.3" - } - ], - "title": "Panel Title", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 0, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 17, - "w": 4, - "x": 12, - "y": 0 - }, - "id": 3, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_metric_values", - "stringInput": "1,2.3" - } - ], - "title": "Panel Title", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 4, - "x": 16, - "y": 0 - }, - "id": 7, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_metric_values", - "stringInput": "1,2.3" - } - ], - "title": "Panel Title", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 0, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 10, - "w": 4, - "x": 20, - "y": 0 - }, - "id": 4, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_metric_values", - "stringInput": "1,2.3" - } - ], - "title": "Panel Title", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 22, - "w": 4, - "x": 16, - "y": 10 - }, - "id": 8, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_metric_values", - "stringInput": "1,2.3" - } - ], - "title": "Panel Title", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 0, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 22, - "w": 4, - "x": 20, - "y": 10 - }, - "id": 9, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_metric_values", - "stringInput": "1,2.3" - } - ], - "title": "Panel Title", - "type": "timeseries" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "decimals": 0, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 15, - "w": 8, - "x": 8, - "y": 17 - }, - "id": 11, - "maxDataPoints": 100, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "title": "Panel Title", - "type": "timeseries" + "type": "dashboard" } - ], - "schemaVersion": 37, - "tags": ["gdev", "panel-tests", "graph-ng"], - "templating": { - "list": [] + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 339, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 32, + "w": 4, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,2.3" + } + ], + "title": "Panel Title", + "type": "timeseries" }, - "time": { - "from": "now-6h", - "to": "now" + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 32, + "w": 4, + "x": 4, + "y": 0 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,2.3" + } + ], + "title": "Panel Title", + "type": "timeseries" }, - "timepicker": {}, - "timezone": "", - "title": "Zero Decimals Y Ticks", - "uid": "kGvJCmGVz", - "version": 7, - "weekStart": "" - } + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 17, + "w": 4, + "x": 8, + "y": 0 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,2.3" + } + ], + "title": "Panel Title", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 17, + "w": 4, + "x": 12, + "y": 0 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,2.3" + } + ], + "title": "Panel Title", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 4, + "x": 16, + "y": 0 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,2.3" + } + ], + "title": "Panel Title", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 4, + "x": 20, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,2.3" + } + ], + "title": "Panel Title", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 22, + "w": 4, + "x": 16, + "y": 10 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,2.3" + } + ], + "title": "Panel Title", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 22, + "w": 4, + "x": 20, + "y": 10 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,2.3" + } + ], + "title": "Panel Title", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 15, + "w": 8, + "x": 8, + "y": 17 + }, + "id": 11, + "maxDataPoints": 100, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "title": "Panel Title", + "type": "timeseries" + } + ], + "schemaVersion": 37, + "tags": ["gdev", "panel-tests", "graph-ng"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Zero Decimals Y Ticks", + "uid": "kGvJCmGVz", + "version": 7, + "weekStart": "" +} diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries-yaxis-ticks.json b/devenv/dev-dashboards/panel-timeseries/timeseries-yaxis-ticks.json index 5cf44cae03f..a877bcc5342 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries-yaxis-ticks.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries-yaxis-ticks.json @@ -28,10 +28,7 @@ { "asDropdown": true, "icon": "external link", - "tags": [ - "gdev", - "graph-ng" - ], + "tags": ["gdev", "graph-ng"], "title": "Graph Tests", "type": "dashboards" } @@ -39,7 +36,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -118,8 +117,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -130,7 +128,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -209,8 +209,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -221,7 +220,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -300,8 +301,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -312,7 +312,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -391,8 +393,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -403,7 +404,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -482,8 +485,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -494,7 +496,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -573,8 +577,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -585,7 +588,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "", "fieldConfig": { "defaults": { @@ -665,8 +670,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -677,7 +681,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "", "fieldConfig": { "defaults": { @@ -757,8 +763,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -769,7 +774,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -848,8 +855,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -860,7 +866,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -935,8 +943,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -947,7 +954,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1022,8 +1031,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1034,7 +1042,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "description": "", "fieldConfig": { "defaults": { @@ -1110,8 +1120,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1122,7 +1131,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1199,8 +1210,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1211,7 +1221,9 @@ "type": "timeseries" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1288,8 +1300,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_metric_values", @@ -1302,11 +1313,7 @@ ], "refresh": "", "schemaVersion": 38, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, @@ -1315,18 +1322,7 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Panel Tests - Graph NG - Y axis ticks", diff --git a/devenv/dev-dashboards/panel-timeseries/timeseries.json b/devenv/dev-dashboards/panel-timeseries/timeseries.json index 38185603968..14d824a3bcd 100644 --- a/devenv/dev-dashboards/panel-timeseries/timeseries.json +++ b/devenv/dev-dashboards/panel-timeseries/timeseries.json @@ -2040,7 +2040,9 @@ "type": "row" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2177,7 +2179,9 @@ "type": "timeseries" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2328,7 +2332,9 @@ "type": "timeseries" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2478,7 +2484,9 @@ "type": "timeseries" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2956,7 +2964,9 @@ "type": "timeseries" }, { - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { diff --git a/devenv/dev-dashboards/panel-trend/trend_example.json b/devenv/dev-dashboards/panel-trend/trend_example.json index 72d9b9b7566..9961b07fc92 100644 --- a/devenv/dev-dashboards/panel-trend/trend_example.json +++ b/devenv/dev-dashboards/panel-trend/trend_example.json @@ -22,7 +22,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -145,8 +147,7 @@ { "csvContent": "Speed (RPM),Torque (NM),Power (kW)\n0,,\n500,,\n800,180,20\n1500,215,40\n2000,227,50\n2500,230,62\n3000,233,75\n3500,236,86\n4000,234,92\n4500,230,100\n5000,220,107\n5500,210,110\n6000,178,106\n6500,120,92\n7000,,\n", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -158,12 +159,7 @@ ], "refresh": "", "schemaVersion": 38, - "tags": [ - "gdev", - "panel-tests", - "graph-ng", - "trend" - ], + "tags": ["gdev", "panel-tests", "graph-ng", "trend"], "templating": { "list": [] }, @@ -177,4 +173,4 @@ "uid": "b36b5576-2e3d-4b0c-8dce-e79514d99345", "version": 4, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-xychart/xychart-demo.json b/devenv/dev-dashboards/panel-xychart/xychart-demo.json index bad99f227cf..f43dc37ba36 100644 --- a/devenv/dev-dashboards/panel-xychart/xychart-demo.json +++ b/devenv/dev-dashboards/panel-xychart/xychart-demo.json @@ -29,8 +29,7 @@ "panels": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -165,8 +164,7 @@ { "csvFileName": "automobiles.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -177,9 +175,7 @@ { "id": "partitionByValues", "options": { - "fields": [ - "Origin" - ] + "fields": ["Origin"] } } ], @@ -187,8 +183,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -282,9 +277,7 @@ "showLegend": true }, "mapping": "auto", - "series": [ - {} - ], + "series": [{}], "tooltip": { "hideZeros": false, "mode": "single", @@ -296,8 +289,7 @@ { "csvFileName": "weight_height.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -308,9 +300,7 @@ { "id": "partitionByValues", "options": { - "fields": [ - "Gender" - ] + "fields": ["Gender"] } } ], @@ -318,8 +308,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "description": "", "fieldConfig": { @@ -407,8 +396,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"executedQueryString\": \"import \\\"influxdata/influxdb/sample\\\"\\nimport \\\"influxdata/influxdb/schema\\\"\\n\\nsample.data(set: \\\"airSensor\\\")\\n |> limit(n: 10)\\n |> group(columns: [\\\"sensor_id\\\"])\\n |> schema.fieldsAsCols()\\n\",\n \"typeVersion\": [\n 0,\n 0\n ]\n },\n \"fields\": [\n {\n \"config\": {},\n \"name\": \"_time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0100\"\n },\n \"name\": \"co\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0100\"\n },\n \"name\": \"humidity\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0100\"\n },\n \"name\": \"temperature\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1686898151000,\n 1686898161000,\n 1686898171000,\n 1686898181000,\n 1686898191000,\n 1686898201000,\n 1686898211000,\n 1686898221000,\n 1686898231000,\n 1686898241000\n ],\n [\n 0.4860747509084685,\n 0.4790778553981873,\n 0.4597786615711249,\n 0.4478751241705323,\n 0.44020548182152397,\n 0.4569020188814074,\n 0.4655250429581913,\n 0.4819032762634117,\n 0.491365303224968,\n 0.5092239049148886\n ],\n [\n 35.084960222673146,\n 35.089101898649055,\n 35.130020139289115,\n 35.10251705413486,\n 35.08184755996438,\n 35.04582600108574,\n 35.09077388700681,\n 35.04096375315356,\n 35.05647864027338,\n 35.091800720894916\n ],\n [\n 71.2187151336704,\n 71.24617135003214,\n 71.21726829714834,\n 71.26528218716341,\n 71.26084702358875,\n 71.24054378677393,\n 71.27884802991244,\n 71.29783163616926,\n 71.33230999794793,\n 71.34975514559837\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"config\": {},\n \"name\": \"_time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0101\"\n },\n \"name\": \"co\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0101\"\n },\n \"name\": \"humidity\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0101\"\n },\n \"name\": \"temperature\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1686898151000,\n 1686898161000,\n 1686898171000,\n 1686898181000,\n 1686898191000,\n 1686898201000,\n 1686898211000,\n 1686898221000,\n 1686898231000,\n 1686898241000\n ],\n [\n 0.4964171323208575,\n 0.48257922637952133,\n 0.46567087322576145,\n 0.4467650568596856,\n 0.4417772514767169,\n 0.4341379534638706,\n 0.439345038257859,\n 0.43452737707188627,\n 0.42417500678639164,\n 0.40704613738954887\n ],\n [\n 34.92662088456207,\n 34.91464944732493,\n 34.95985887959429,\n 34.929238294894844,\n 34.954103486412336,\n 34.98895514808448,\n 35.03476240219413,\n 35.028863365125844,\n 35.03717014192905,\n 35.00775919996651\n ],\n [\n 71.83521495327129,\n 71.83370684393908,\n 71.83712160725877,\n 71.88299650060345,\n 71.90526594972503,\n 71.8631768515712,\n 71.85577028152356,\n 71.89245169322045,\n 71.93606971457449,\n 71.96164771829956\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"config\": {},\n \"name\": \"_time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0102\"\n },\n \"name\": \"co\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0102\"\n },\n \"name\": \"humidity\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0102\"\n },\n \"name\": \"temperature\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1686898151000,\n 1686898161000,\n 1686898171000,\n 1686898181000,\n 1686898191000,\n 1686898201000,\n 1686898211000,\n 1686898221000,\n 1686898231000,\n 1686898241000\n ],\n [\n 0.4870365157212326,\n 0.5032215395360725,\n 0.5035259485542879,\n 0.4841798164121083,\n 0.483812619615984,\n 0.4882993499867786,\n 0.4719009580435705,\n 0.4700689455810135,\n 0.48112413053169706,\n 0.4691106107162724\n ],\n [\n 34.86359615237462,\n 34.84629630959152,\n 34.81771460191974,\n 34.854640808896036,\n 34.86699022367547,\n 34.864319179513096,\n 34.86915527122888,\n 34.89889649251399,\n 34.86576388906259,\n 34.88622805723735\n ],\n [\n 72.03972207916735,\n 72.05566134520713,\n 72.08473708469143,\n 72.07306942754916,\n 72.0427409958616,\n 72.04536459775873,\n 72.09400368933838,\n 72.14293243585941,\n 72.18840859469728,\n 72.15524663568557\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"config\": {},\n \"name\": \"_time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0103\"\n },\n \"name\": \"co\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0103\"\n },\n \"name\": \"humidity\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0103\"\n },\n \"name\": \"temperature\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1686898151000,\n 1686898161000,\n 1686898171000,\n 1686898181000,\n 1686898191000,\n 1686898201000,\n 1686898211000,\n 1686898221000,\n 1686898231000,\n 1686898241000\n ],\n [\n 0.3979338437168368,\n 0.4117117830425588,\n 0.4000376537804249,\n 0.40114553332960723,\n 0.3909759394807085,\n 0.38213452854680874,\n 0.37339826793170855,\n 0.3624713623472977,\n 0.35640734768856297,\n 0.35690557816119645\n ],\n [\n 35.16192242281515,\n 35.19891757080395,\n 35.23754373785834,\n 35.25270355198698,\n 35.29525947947623,\n 35.272026086051184,\n 35.275233467451635,\n 35.23834593104291,\n 35.25233833634368,\n 35.22053598631417\n ],\n [\n 71.29169927438586,\n 71.32114142326272,\n 71.33632903748085,\n 71.3064635464553,\n 71.26436580075855,\n 71.30431517798449,\n 71.28861822950174,\n 71.26778760430453,\n 71.23481860950403,\n 71.23084180913762\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"config\": {},\n \"name\": \"_time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0200\"\n },\n \"name\": \"co\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0200\"\n },\n \"name\": \"humidity\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0200\"\n },\n \"name\": \"temperature\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1686898151000,\n 1686898161000,\n 1686898171000,\n 1686898181000,\n 1686898191000,\n 1686898201000,\n 1686898211000,\n 1686898221000,\n 1686898231000,\n 1686898241000\n ],\n [\n 0.5129015632374951,\n 0.552968955740271,\n 0.5690444135565038,\n 0.6050221760178951,\n 0.5943904374498042,\n 0.6153588301241533,\n 0.6164375823908576,\n 0.6059635952981327,\n 0.6470283318343141,\n 0.6861075098169984\n ],\n [\n 35.75385592664215,\n 35.753218714107504,\n 35.72547059379115,\n 35.74553237372676,\n 35.73378268235891,\n 35.747560005048086,\n 35.769867599816735,\n 35.73929899563808,\n 35.705317880681875,\n 35.65611547721327\n ],\n [\n 73.64806558624832,\n 73.65728412124716,\n 73.68805520135102,\n 73.65185281162863,\n 73.63499402146681,\n 73.68359308763364,\n 73.64682180062367,\n 73.63298265031713,\n 73.59096788790947,\n 73.6381949597034\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"config\": {},\n \"name\": \"_time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0201\"\n },\n \"name\": \"co\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0201\"\n },\n \"name\": \"humidity\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0201\"\n },\n \"name\": \"temperature\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1686898151000,\n 1686898161000,\n 1686898171000,\n 1686898181000,\n 1686898191000,\n 1686898201000,\n 1686898211000,\n 1686898221000,\n 1686898231000,\n 1686898241000\n ],\n [\n 0.5082489903097206,\n 0.525490999229553,\n 0.5300688563597754,\n 0.5351282770519872,\n 0.533437724470923,\n 0.5164188260958164,\n 0.5134188770545406,\n 0.4949993424337248,\n 0.4960387472164697,\n 0.5046934441184395\n ],\n [\n 35.24972613768055,\n 35.238916979727335,\n 35.28758958750148,\n 35.26413172906637,\n 35.2875488141577,\n 35.32420268624064,\n 35.2932945023372,\n 35.277959228995584,\n 35.27901195680498,\n 35.27056468599647\n ],\n [\n 73.99618150289902,\n 74.01973867729899,\n 73.97488048626532,\n 73.97027021897547,\n 73.96055098802299,\n 73.94544160099603,\n 73.91010608315078,\n 73.9594600253564,\n 73.93323000030807,\n 73.94265737589377\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"config\": {},\n \"name\": \"_time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0202\"\n },\n \"name\": \"co\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0202\"\n },\n \"name\": \"humidity\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0202\"\n },\n \"name\": \"temperature\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1686898151000,\n 1686898161000,\n 1686898171000,\n 1686898181000,\n 1686898191000,\n 1686898201000,\n 1686898211000,\n 1686898221000,\n 1686898231000,\n 1686898241000\n ],\n [\n 0.4843506884938821,\n 0.48132272563110234,\n 0.4738268718199932,\n 0.4929629619836042,\n 0.4730642763470896,\n 0.4654986284178103,\n 0.47939137148137106,\n 0.4729232244284411,\n 0.48941836624955776,\n 0.49438537058971027\n ],\n [\n 35.6843507637763,\n 35.67052290561625,\n 35.651191124511904,\n 35.682694567957775,\n 35.71754463951193,\n 35.73271359197846,\n 35.76201128140823,\n 35.72031962293351,\n 35.76508898680512,\n 35.77376488207177\n ],\n [\n 75.28536533620796,\n 75.3203884403274,\n 75.32915092826639,\n 75.3260291876276,\n 75.29613126188146,\n 75.31098782530198,\n 75.27434784939884,\n 75.30929861509146,\n 75.32818662869892,\n 75.33650169518009\n ]\n ]\n }\n },\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"config\": {},\n \"name\": \"_time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0203\"\n },\n \"name\": \"co\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0203\"\n },\n \"name\": \"humidity\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n },\n {\n \"config\": {},\n \"labels\": {\n \"sensor_id\": \"TLM0203\"\n },\n \"name\": \"temperature\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1686898151000,\n 1686898161000,\n 1686898171000,\n 1686898181000,\n 1686898191000,\n 1686898201000,\n 1686898211000,\n 1686898221000,\n 1686898231000,\n 1686898241000\n ],\n [\n 0.39373421338928505,\n 0.40929905142175416,\n 0.39727569840202215,\n 0.415834035946844,\n 0.413865826090914,\n 0.4271278288285897,\n 0.435277474574255,\n 0.4480042029285831,\n 0.462641109361364,\n 0.4566678920783633\n ],\n [\n 35.86322585633077,\n 35.88732568460068,\n 35.87553827435961,\n 35.84063967239316,\n 35.813642046057005,\n 35.8569165461967,\n 35.80924651587166,\n 35.80599199551315,\n 35.75906423980926,\n 35.79948361256034\n ],\n [\n 74.78112365229704,\n 74.73821283157658,\n 74.73019922701285,\n 74.73280967321202,\n 74.74885460260536,\n 74.70880911705714,\n 74.73346687605624,\n 74.77682413662681,\n 74.80562351134131,\n 74.82881664528719\n ]\n ]\n }\n }\n]", "refId": "A", @@ -420,8 +408,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -523,8 +510,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -535,8 +521,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -671,8 +656,7 @@ { "csvFileName": "automobiles.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -683,9 +667,7 @@ { "id": "partitionByValues", "options": { - "fields": [ - "Origin" - ] + "fields": ["Origin"] } } ], @@ -693,8 +675,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -796,8 +777,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -808,8 +788,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -917,8 +896,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -929,8 +907,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1028,8 +1005,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -1040,8 +1016,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1137,8 +1112,7 @@ { "csvContent": "X,Y\n725.1,435.6\n734.1,497.2\n714.3,527.7\n683.5,548.7\n601.8,594.0\n598.5,621.7\n573.9,644.7\n525.7,695.7\n477.2,732.8\n411.8,755.3\n353.6,758.3\n422.6,736.5\n455.3,724.1\n479.2,699.2\n474.0,673.8\n434.5,662.1\n362.2,679.8\n311.2,698.8\n260.1,728.9\n213.4,771.1\n176.2,818.0\n211.2,742.6\n253.9,707.9\n309.9,668.8\n374.7,643.2\n322.8,629.9\n277.1,607.1\n237.0,616.8\n188.9,613.9\n143.0,594.1\n101.8,566.4\n178.1,590.2\n222.2,575.9\n187.9,549.1\n161.5,517.5\n128.6,506.8\n97.3,488.3\n62.4,436.0\n99.6,473.8\n138.3,477.0\n125.0,396.7\n95.6,359.2\n83.6,322.1\n81.0,289.7\n104.0,343.8\n129.3,358.4\n151.2,291.1\n124.0,242.6\n126.3,170.2\n133.7,212.8\n148.4,243.3\n167.9,262.7\n209.1,205.7\n230.1,150.3\n231.4,120.1\n316.0,120.1\n400.6,120.1\n485.2,120.1\n569.8,120.1\n569.3,166.4\n553.0,205.5\n489.2,265.7\n422.2,309.1\n353.7,343.1\n328.2,386.3\n321.6,432.6\n334.1,473.1\n357.6,500.3\n389.9,508.5\n418.8,479.9\n447.9,413.3\n480.0,379.0\n521.6,354.2\n583.9,351.6\n549.7,357.7\n571.6,376.0\n517.1,380.8\n550.3,393.2\n504.3,402.3\n489.5,425.8\n527.5,425.8\n472.7,457.2\n447.1,523.8\n538.6,435.7\n598.4,403.7\n697.8,349.1\n645.3,390.4\n712.3,373.8\n586.0,424.1\n526.9,463.3\n469.5,538.0\n540.6,477.5\n531.2,528.4\n598.6,460.1\n594.5,509.0\n651.2,460.1\n649.9,502.6\n699.4,446.1\n707.6,477.1\n722.0,442.9", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -1149,8 +1123,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1206,10 +1179,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 10, - 15 - ], + "dash": [10, 15], "fill": "dash" } } @@ -1292,9 +1262,7 @@ "showLegend": true }, "mapping": "auto", - "series": [ - {} - ], + "series": [{}], "tooltip": { "hideZeros": false, "mode": "single", @@ -1307,8 +1275,7 @@ "alias": "", "csvContent": "x,cos(x)\n-7.5,3.4663531783502584\n-7.45,3.9308257356494076\n-7.4,4.385473275743903\n-7.35,4.829159416559378\n-7.3,5.260775173811053\n-7.25,5.679241732886949\n-7.2,6.083513145322545\n-7.15,6.472578943127236\n-7.1,6.845466664428066\n-7.05,7.201244284117942\n-7,7.539022543433046\n-6.95,7.857957172636611\n-6.9,8.157251001253568\n-6.85,8.436155950581597\n-6.8,8.693974903498253\n-6.75,8.930063446890767\n-6.7,9.143831482353194\n-6.65,9.334744701125118\n-6.6,9.502325919585296\n-6.55,9.64615627196218\n-6.5,9.765876257280235\n-6.45,9.861186637925126\n-6.4,9.931849187581927\n-6.35,9.97768728667684\n-6.3,9.998586363834152\n-6.25,9.994494182244994\n-6.2,9.965420970232175\n-6.15,9.91143939568469\n-6.1,9.832684384425844\n-6.05,9.729352782968974\n-6,9.601702866503661\n-5.95,9.450053693342275\n-5.9,9.27478430744036\n-5.85,9.076332790984132\n-5.8,8.85519516941319\n-5.75,8.611924171615208\n-5.7,8.347127848391597\n-5.65,8.061468052647157\n-5.6,7.755658785102496\n-5.55,7.430464409664099\n-5.5,7.0866977429126\n-5.45,6.725218022484659\n-5.4,6.346928759426347\n-5.35,5.952775479886061\n-5.3,5.543743361791607\n-5.25,5.120854772418407\n-5.2,4.685166713003771\n-5.15,4.237768176794282\n-5.1,3.7797774271298024\n-5.05,3.3123392023675367\n-5,2.8366218546322624\n-4.95,2.353814429544512\n-4.9,1.8651236942257576\n-4.85,1.371771121009073\n-4.8,0.874989834394464\n-4.75,0.37602152887976553\n-4.7,-0.1238866346289056\n-4.65,-0.6234851460699166\n-4.6,-1.1215252693505486\n-4.55,-1.616762163536865\n-4.5,-2.107957994307797\n-4.45,-2.593885027896261\n-4.4,-3.0733286997841933\n-4.35,-3.5450906504813195\n-4.3,-4.007991720799755\n-4.25,-4.460874899137928\n-4.2,-4.902608213406994\n-4.15,-5.332087560371543\n-4.1,-5.748239465332691\n-4.05,-6.150023765255744\n-4,-6.536436208636119\n-3.95,-6.906510965605075\n-3.9,-7.259323042001402\n-3.85,-7.593990591375079\n-3.8,-7.909677119144169\n-3.75,-8.205593573395607\n-3.7,-8.48100031710408\n-3.65,-8.73520897683938\n-3.6,-8.96758416334147\n-3.55,-9.177545059662759\n-3.5,-9.364566872907963\n-3.45,-9.528182145943047\n-3.4,-9.66798192579461\n-3.35,-9.78361678581934\n-3.3,-9.87479769908865\n-3.25,-9.941296760805463\n-3.2,-9.982947757947532\n-3.15,-9.99964658471342\n-3.1,-9.991351502732794\n-3.05,-9.958083245390611\n-3,-9.899924966004454\n-2.95,-9.81702202998454\n-2.9,-9.709581651495906\n-2.85,-9.577872375530903\n-2.8,-9.42222340668658\n-2.75,-9.243023786324635\n-2.7,-9.040721420170613\n-2.65,-8.815821958782859\n-2.6,-8.568887533689473\n-2.55,-8.30053535235222\n-2.5,-8.011436155469337\n-2.45,-7.702312540473074\n-2.4,-7.373937155412454\n-2.35,-7.027130767735539\n-2.3,-6.66276021279824\n-2.25,-6.281736227227391\n-2.2,-5.885011172553458\n-2.15,-5.473576654802709\n-2.1,-5.048461045998575\n-2.05,-4.610726913767127\n-2,-4.161468365471424\n-1.95,-3.7018083135128688\n-1.9,-3.2328956686350336\n-1.85,-2.7559024682451296\n-1.8,-2.272020946930871\n-1.75,-1.7824605564949207\n-1.7,-1.2884449429552465\n-1.65,-0.7912088880673386\n-1.6,-0.29199522301288816\n-1.55,0.20794827803092428\n-1.5,0.7073720166770291\n-1.45,1.2050276936736661\n-1.4,1.6996714290024104\n-1.35,2.1900668709304147\n-1.3,2.6749882862458736\n-1.25,3.1532236239526865\n-1.2,3.623577544766736\n-1.15,4.084874408841574\n-1.1,4.5359612142557735\n-1.05,4.97571047891727\n-1,5.403023058681398\n-0.95,5.8168308946388345\n-0.9,6.216099682706644\n-0.85,6.599831458849822\n-0.8,6.967067093471654\n-0.75,7.316888688738209\n-0.7,7.648421872844885\n-0.65,7.960837985490558\n-0.6,8.253356149096783\n-0.55,8.525245220595057\n-0.5,8.775825618903728\n-0.45,9.004471023526769\n-0.4,9.210609940028851\n-0.35,9.393727128473788\n-0.3,9.55336489125606\n-0.25,9.689124217106448\n-0.2,9.800665778412416\n-0.15,9.887710779360422\n-0.1,9.950041652780257\n-0.05,9.987502603949663\n0,10\n0.05,9.987502603949663\n0.1,9.950041652780257\n0.15,9.887710779360422\n0.2,9.800665778412416\n0.25,9.689124217106448\n0.3,9.55336489125606\n0.35,9.393727128473788\n0.4,9.210609940028851\n0.45,9.004471023526769\n0.5,8.775825618903728\n0.55,8.525245220595057\n0.6,8.253356149096783\n0.65,7.960837985490558\n0.7,7.648421872844885\n0.75,7.316888688738209\n0.8,6.967067093471654\n0.85,6.599831458849822\n0.9,6.216099682706644\n0.95,5.8168308946388345\n1,5.403023058681398\n1.05,4.97571047891727\n1.1,4.5359612142557735\n1.15,4.084874408841574\n1.2,3.623577544766736\n1.25,3.1532236239526865\n1.3,2.6749882862458736\n1.35,2.1900668709304147\n1.4,1.6996714290024104\n1.45,1.2050276936736661\n1.5,0.7073720166770291\n1.55,0.20794827803092428\n1.6,-0.29199522301288816\n1.65,-0.7912088880673386\n1.7,-1.2884449429552465\n1.75,-1.7824605564949207\n1.8,-2.272020946930871\n1.85,-2.7559024682451296\n1.9,-3.2328956686350336\n1.95,-3.7018083135128688\n2,-4.161468365471424\n2.05,-4.610726913767127\n2.1,-5.048461045998575\n2.15,-5.473576654802709\n2.2,-5.885011172553458\n2.25,-6.281736227227391\n2.3,-6.66276021279824\n2.35,-7.027130767735539\n2.4,-7.373937155412454\n2.45,-7.702312540473074\n2.5,-8.011436155469337\n2.55,-8.30053535235222\n2.6,-8.568887533689473\n2.65,-8.815821958782859\n2.7,-9.040721420170613\n2.75,-9.243023786324635\n2.8,-9.42222340668658\n2.85,-9.577872375530903\n2.9,-9.709581651495906\n2.95,-9.81702202998454\n3,-9.899924966004454\n3.05,-9.958083245390611\n3.1,-9.991351502732794\n3.15,-9.99964658471342\n3.2,-9.982947757947532\n3.25,-9.941296760805463\n3.3,-9.87479769908865\n3.35,-9.78361678581934\n3.4,-9.66798192579461\n3.45,-9.528182145943047\n3.5,-9.364566872907963\n3.55,-9.177545059662759\n3.6,-8.96758416334147\n3.65,-8.73520897683938\n3.7,-8.48100031710408\n3.75,-8.205593573395607\n3.8,-7.909677119144169\n3.85,-7.593990591375079\n3.9,-7.259323042001402\n3.95,-6.906510965605075\n4,-6.536436208636119\n4.05,-6.150023765255744\n4.1,-5.748239465332691\n4.15,-5.332087560371543\n4.2,-4.902608213406994\n4.25,-4.460874899137928\n4.3,-4.007991720799755\n4.35,-3.5450906504813195\n4.4,-3.0733286997841933\n4.45,-2.593885027896261\n4.5,-2.107957994307797\n4.55,-1.616762163536865\n4.6,-1.1215252693505486\n4.65,-0.6234851460699166\n4.7,-0.1238866346289056\n4.75,0.37602152887976553\n4.8,0.874989834394464\n4.85,1.371771121009073\n4.9,1.8651236942257576\n4.95,2.353814429544512\n5,2.8366218546322624\n5.05,3.3123392023675367\n5.1,3.7797774271298024\n5.15,4.237768176794282\n5.2,4.685166713003771\n5.25,5.120854772418407\n5.3,5.543743361791607\n5.35,5.952775479886061\n5.4,6.346928759426347\n5.45,6.725218022484659\n5.5,7.0866977429126\n5.55,7.430464409664099\n5.6,7.755658785102496\n5.65,8.061468052647157\n5.7,8.347127848391597\n5.75,8.611924171615208\n5.8,8.85519516941319\n5.85,9.076332790984132\n5.9,9.27478430744036\n5.95,9.450053693342275\n6,9.601702866503661\n6.05,9.729352782968974\n6.1,9.832684384425844\n6.15,9.91143939568469\n6.2,9.965420970232175\n6.25,9.994494182244994\n6.3,9.998586363834152\n6.35,9.97768728667684\n6.4,9.931849187581927\n6.45,9.861186637925126\n6.5,9.765876257280235\n6.55,9.64615627196218\n6.6,9.502325919585296\n6.65,9.334744701125118\n6.7,9.143831482353194\n6.75,8.930063446890767\n6.8,8.693974903498253\n6.85,8.436155950581597\n6.9,8.157251001253568\n6.95,7.857957172636611\n7,7.539022543433046\n7.05,7.201244284117942\n7.1,6.845466664428066\n7.15,6.472578943127236\n7.2,6.083513145322545\n7.25,5.679241732886949\n7.3,5.260775173811053\n7.35,4.829159416559378\n7.4,4.385473275743903\n7.45,3.9308257356494076", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -1316,8 +1283,7 @@ { "csvContent": "x2,x^2\n-7.5,56.25\n-7.45,55.502500000000005\n-7.4,54.760000000000005\n-7.35,54.022499999999994\n-7.3,53.29\n-7.25,52.5625\n-7.2,51.84\n-7.15,51.1225\n-7.1,50.41\n-7.05,49.7025\n-7,49\n-6.95,48.3025\n-6.9,47.61000000000001\n-6.85,46.92249999999999\n-6.8,46.239999999999995\n-6.75,45.5625\n-6.7,44.89\n-6.65,44.222500000000004\n-6.6,43.559999999999995\n-6.55,42.902499999999996\n-6.5,42.25\n-6.45,41.6025\n-6.4,40.96000000000001\n-6.35,40.3225\n-6.3,39.69\n-6.25,39.0625\n-6.2,38.440000000000005\n-6.15,37.822500000000005\n-6.1,37.209999999999994\n-6.05,36.6025\n-6,36\n-5.95,35.4025\n-5.9,34.81\n-5.85,34.2225\n-5.8,33.64\n-5.75,33.0625\n-5.7,32.49\n-5.65,31.922500000000003\n-5.6,31.359999999999996\n-5.55,30.8025\n-5.5,30.25\n-5.45,29.7025\n-5.4,29.160000000000004\n-5.35,28.622499999999995\n-5.3,28.09\n-5.25,27.5625\n-5.2,27.040000000000003\n-5.15,26.522500000000004\n-5.1,26.009999999999998\n-5.05,25.502499999999998\n-5,25\n-4.95,24.5025\n-4.9,24.010000000000005\n-4.85,23.522499999999997\n-4.8,23.04\n-4.75,22.5625\n-4.7,22.090000000000003\n-4.65,21.622500000000002\n-4.6,21.159999999999997\n-4.55,20.702499999999997\n-4.5,20.25\n-4.45,19.802500000000002\n-4.4,19.360000000000003\n-4.35,18.922499999999996\n-4.3,18.49\n-4.25,18.0625\n-4.2,17.64\n-4.15,17.222500000000004\n-4.1,16.81\n-4.05,16.4025\n-4,16\n-3.95,15.602500000000001\n-3.9,15.209999999999999\n-3.85,14.822500000000002\n-3.8,14.44\n-3.75,14.0625\n-3.7,13.690000000000001\n-3.65,13.3225\n-3.6,12.96\n-3.55,12.6025\n-3.5,12.25\n-3.45,11.902500000000002\n-3.4,11.559999999999999\n-3.35,11.2225\n-3.3,10.889999999999999\n-3.25,10.5625\n-3.2,10.240000000000002\n-3.15,9.9225\n-3.1,9.610000000000001\n-3.05,9.302499999999998\n-3,9\n-2.95,8.7025\n-2.9,8.41\n-2.85,8.1225\n-2.8,7.839999999999999\n-2.75,7.5625\n-2.7,7.290000000000001\n-2.65,7.0225\n-2.6,6.760000000000001\n-2.55,6.5024999999999995\n-2.5,6.25\n-2.45,6.002500000000001\n-2.4,5.76\n-2.35,5.522500000000001\n-2.3,5.289999999999999\n-2.25,5.0625\n-2.2,4.840000000000001\n-2.15,4.6225\n-2.1,4.41\n-2.05,4.2025\n-2,4\n-1.95,3.8024999999999998\n-1.9,3.61\n-1.85,3.4225000000000003\n-1.8,3.24\n-1.75,3.0625\n-1.7,2.8899999999999997\n-1.65,2.7224999999999997\n-1.6,2.5600000000000005\n-1.55,2.4025000000000003\n-1.5,2.25\n-1.45,2.1025\n-1.4,1.9599999999999997\n-1.35,1.8225000000000002\n-1.3,1.6900000000000002\n-1.25,1.5625\n-1.2,1.44\n-1.15,1.3224999999999998\n-1.1,1.2100000000000002\n-1.05,1.1025\n-1,1\n-0.95,0.9025\n-0.9,0.81\n-0.85,0.7224999999999999\n-0.8,0.6400000000000001\n-0.75,0.5625\n-0.7,0.48999999999999994\n-0.65,0.42250000000000004\n-0.6,0.36\n-0.55,0.30250000000000005\n-0.5,0.25\n-0.45,0.2025\n-0.4,0.16000000000000003\n-0.35,0.12249999999999998\n-0.3,0.09\n-0.25,0.0625\n-0.2,0.04000000000000001\n-0.15,0.0225\n-0.1,0.010000000000000002\n-0.05,0.0025000000000000005\n0,0\n0.05,0.0025000000000000005\n0.1,0.010000000000000002\n0.15,0.0225\n0.2,0.04000000000000001\n0.25,0.0625\n0.3,0.09\n0.35,0.12249999999999998\n0.4,0.16000000000000003\n0.45,0.2025\n0.5,0.25\n0.55,0.30250000000000005\n0.6,0.36\n0.65,0.42250000000000004\n0.7,0.48999999999999994\n0.75,0.5625\n0.8,0.6400000000000001\n0.85,0.7224999999999999\n0.9,0.81\n0.95,0.9025\n1,1\n1.05,1.1025\n1.1,1.2100000000000002\n1.15,1.3224999999999998\n1.2,1.44\n1.25,1.5625\n1.3,1.6900000000000002\n1.35,1.8225000000000002\n1.4,1.9599999999999997\n1.45,2.1025\n1.5,2.25\n1.55,2.4025000000000003\n1.6,2.5600000000000005\n1.65,2.7224999999999997\n1.7,2.8899999999999997\n1.75,3.0625\n1.8,3.24\n1.85,3.4225000000000003\n1.9,3.61\n1.95,3.8024999999999998\n2,4\n2.05,4.2025\n2.1,4.41\n2.15,4.6225\n2.2,4.840000000000001\n2.25,5.0625\n2.3,5.289999999999999\n2.35,5.522500000000001\n2.4,5.76\n2.45,6.002500000000001\n2.5,6.25\n2.55,6.5024999999999995\n2.6,6.760000000000001\n2.65,7.0225\n2.7,7.290000000000001\n2.75,7.5625\n2.8,7.839999999999999\n2.85,8.1225\n2.9,8.41\n2.95,8.7025\n3,9\n3.05,9.302499999999998\n3.1,9.610000000000001\n3.15,9.9225\n3.2,10.240000000000002\n3.25,10.5625\n3.3,10.889999999999999\n3.35,11.2225\n3.4,11.559999999999999\n3.45,11.902500000000002\n3.5,12.25\n3.55,12.6025\n3.6,12.96\n3.65,13.3225\n3.7,13.690000000000001\n3.75,14.0625\n3.8,14.44\n3.85,14.822500000000002\n3.9,15.209999999999999\n3.95,15.602500000000001\n4,16\n4.05,16.4025\n4.1,16.81\n4.15,17.222500000000004\n4.2,17.64\n4.25,18.0625\n4.3,18.49\n4.35,18.922499999999996\n4.4,19.360000000000003\n4.45,19.802500000000002\n4.5,20.25\n4.55,20.702499999999997\n4.6,21.159999999999997\n4.65,21.622500000000002\n4.7,22.090000000000003\n4.75,22.5625\n4.8,23.04\n4.85,23.522499999999997\n4.9,24.010000000000005\n4.95,24.5025\n5,25\n5.05,25.502499999999998\n5.1,26.009999999999998\n5.15,26.522500000000004\n5.2,27.040000000000003\n5.25,27.5625\n5.3,28.09\n5.35,28.622499999999995\n5.4,29.160000000000004\n5.45,29.7025\n5.5,30.25\n5.55,30.8025\n5.6,31.359999999999996\n5.65,31.922500000000003\n5.7,32.49\n5.75,33.0625\n5.8,33.64\n5.85,34.2225\n5.9,34.81\n5.95,35.4025\n6,36\n6.05,36.6025\n6.1,37.209999999999994\n6.15,37.822500000000005\n6.2,38.440000000000005\n6.25,39.0625\n6.3,39.69\n6.35,40.3225\n6.4,40.96000000000001\n6.45,41.6025\n6.5,42.25\n6.55,42.902499999999996\n6.6,43.559999999999995\n6.65,44.222500000000004\n6.7,44.89\n6.75,45.5625\n6.8,46.239999999999995\n6.85,46.92249999999999\n6.9,47.61000000000001\n6.95,48.3025\n7,49\n7.05,49.7025\n7.1,50.41\n7.15,51.1225\n7.2,51.84\n7.25,52.5625\n7.3,53.29\n7.35,54.022499999999994\n7.4,54.760000000000005\n7.45,55.502500000000005", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_content" @@ -1325,8 +1291,7 @@ { "csvContent": "x3,sqrt(x)\n0,0\n0.05,2.23606797749979\n0.1,3.1622776601683795\n0.15,3.872983346207417\n0.2,4.47213595499958\n0.25,5\n0.3,5.47722557505166\n0.35,5.916079783099616\n0.4,6.324555320336759\n0.45,6.708203932499369\n0.5,7.0710678118654755\n0.55,7.416198487095663\n0.6,7.745966692414834\n0.65,8.062257748298551\n0.7,8.366600265340756\n0.75,8.660254037844386\n0.8,8.94427190999916\n0.85,9.219544457292887\n0.9,9.486832980505138\n0.95,9.746794344808963\n1,10\n1.05,10.2469507659596\n1.1,10.488088481701517\n1.15,10.723805294763608\n1.2,10.95445115010332\n1.25,11.180339887498949\n1.3,11.401754250991381\n1.35,11.618950038622252\n1.4,11.832159566199232\n1.45,12.041594578792296\n1.5,12.24744871391589\n1.55,12.449899597988733\n1.6,12.649110640673518\n1.65,12.84523257866513\n1.7,13.038404810405297\n1.75,13.228756555322953\n1.8,13.416407864998739\n1.85,13.601470508735442\n1.9,13.784048752090222\n1.95,13.96424004376894\n2,14.142135623730951\n2.05,14.317821063276352\n2.1,14.49137674618944\n2.15,14.66287829861518\n2.2,14.832396974191326\n2.25,15\n2.3,15.1657508881031\n2.35,15.329709716755893\n2.4,15.491933384829668\n2.45,15.652475842498529\n2.5,15.811388300841898\n2.55,15.968719422671311\n2.6,16.124515496597102\n2.65,16.278820596099706\n2.7,16.431676725154986\n2.75,16.583123951776997\n2.8,16.73320053068151\n2.85,16.881943016134134\n2.9,17.0293863659264\n2.95,17.175564037317667\n3,17.32050807568877\n3.05,17.46424919657298\n3.1,17.60681686165901\n3.15,17.74823934929885\n3.2,17.88854381999832\n3.25,18.027756377319946\n3.3,18.16590212458495\n3.35,18.303005217723125\n3.4,18.439088914585774\n3.45,18.57417562100671\n3.5,18.708286933869708\n3.55,18.84144368141677\n3.6,18.973665961010276\n3.65,19.1049731745428\n3.7,19.235384061671347\n3.75,19.364916731037084\n3.8,19.493588689617926\n3.85,19.621416870348586\n3.9,19.748417658131498\n3.95,19.87460691435179\n4,20\n4.05,20.124611797498105\n4.1,20.248456731316583\n4.15,20.37154878746336\n4.2,20.4939015319192\n4.25,20.615528128088304\n4.3,20.73644135332772\n4.35,20.85665361461421\n4.4,20.976176963403034\n4.45,21.095023109728984\n4.5,21.213203435596423\n4.55,21.330729007701542\n4.6,21.447610589527216\n4.65,21.563858652847827\n4.7,21.6794833886788\n4.75,21.79449471770337\n4.8,21.90890230020664\n4.85,22.022715545545243\n4.9,22.135943621178654\n4.95,22.24859546128699\n5,22.360679774997898\n5.05,22.47220505424423\n5.1,22.58317958127243\n5.15,22.693611435820436\n5.2,22.803508501982762\n5.25,22.9128784747792\n5.3,23.021728866442675\n5.35,23.130067012440755\n5.4,23.237900077244504\n5.45,23.345235059857504\n5.5,23.45207879911715\n5.55,23.55843797877949\n5.6,23.664319132398465\n5.65,23.769728648009426\n5.7,23.874672772626646\n5.75,23.979157616563597\n5.8,24.08318915758459\n5.85,24.186773244895647\n5.9,24.289915602982237\n5.95,24.392621835300936\n6,24.49489742783178\n6.05,24.596747752497684\n6.1,24.698178070456937\n6.15,24.79919353527449\n6.2,24.899799195977465\n6.25,25\n6.3,25.099800796022265\n6.35,25.199206336708304\n6.4,25.298221281347036\n6.45,25.39685019840059\n6.5,25.495097567963924\n6.55,25.592967784139454\n6.6,25.69046515733026\n6.65,25.787593916455258\n6.7,25.88435821108957\n6.75,25.98076211353316\n6.8,26.076809620810593\n6.85,26.1725046566048\n6.9,26.267851073127396\n6.95,26.362852652928133\n7,26.457513110645905\n7.05,26.551836094703507\n7.1,26.645825188948457\n7.15,26.739483914241873\n7.2,26.832815729997478\n7.25,26.92582403567252\n7.3,27.018512172212592\n7.35,27.11088342345192\n7.4,27.202941017470884\n7.45,27.294688127912362", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "csv_content" @@ -1334,8 +1299,7 @@ { "csvContent": "x4,-sqrt(x)\n0,0\n0.05,-2.23606797749979\n0.1,-3.1622776601683795\n0.15,-3.872983346207417\n0.2,-4.47213595499958\n0.25,-5\n0.3,-5.47722557505166\n0.35,-5.916079783099616\n0.4,-6.324555320336759\n0.45,-6.708203932499369\n0.5,-7.0710678118654755\n0.55,-7.416198487095663\n0.6,-7.745966692414834\n0.65,-8.062257748298551\n0.7,-8.366600265340756\n0.75,-8.660254037844386\n0.8,-8.94427190999916\n0.85,-9.219544457292887\n0.9,-9.486832980505138\n0.95,-9.746794344808963\n1,-10\n1.05,-10.2469507659596\n1.1,-10.488088481701517\n1.15,-10.723805294763608\n1.2,-10.95445115010332\n1.25,-11.180339887498949\n1.3,-11.401754250991381\n1.35,-11.618950038622252\n1.4,-11.832159566199232\n1.45,-12.041594578792296\n1.5,-12.24744871391589\n1.55,-12.449899597988733\n1.6,-12.649110640673518\n1.65,-12.84523257866513\n1.7,-13.038404810405297\n1.75,-13.228756555322953\n1.8,-13.416407864998739\n1.85,-13.601470508735442\n1.9,-13.784048752090222\n1.95,-13.96424004376894\n2,-14.142135623730951\n2.05,-14.317821063276352\n2.1,-14.49137674618944\n2.15,-14.66287829861518\n2.2,-14.832396974191326\n2.25,-15\n2.3,-15.1657508881031\n2.35,-15.329709716755893\n2.4,-15.491933384829668\n2.45,-15.652475842498529\n2.5,-15.811388300841898\n2.55,-15.968719422671311\n2.6,-16.124515496597102\n2.65,-16.278820596099706\n2.7,-16.431676725154986\n2.75,-16.583123951776997\n2.8,-16.73320053068151\n2.85,-16.881943016134134\n2.9,-17.0293863659264\n2.95,-17.175564037317667\n3,-17.32050807568877\n3.05,-17.46424919657298\n3.1,-17.60681686165901\n3.15,-17.74823934929885\n3.2,-17.88854381999832\n3.25,-18.027756377319946\n3.3,-18.16590212458495\n3.35,-18.303005217723125\n3.4,-18.439088914585774\n3.45,-18.57417562100671\n3.5,-18.708286933869708\n3.55,-18.84144368141677\n3.6,-18.973665961010276\n3.65,-19.1049731745428\n3.7,-19.235384061671347\n3.75,-19.364916731037084\n3.8,-19.493588689617926\n3.85,-19.621416870348586\n3.9,-19.748417658131498\n3.95,-19.87460691435179\n4,-20\n4.05,-20.124611797498105\n4.1,-20.248456731316583\n4.15,-20.37154878746336\n4.2,-20.4939015319192\n4.25,-20.615528128088304\n4.3,-20.73644135332772\n4.35,-20.85665361461421\n4.4,-20.976176963403034\n4.45,-21.095023109728984\n4.5,-21.213203435596423\n4.55,-21.330729007701542\n4.6,-21.447610589527216\n4.65,-21.563858652847827\n4.7,-21.6794833886788\n4.75,-21.79449471770337\n4.8,-21.90890230020664\n4.85,-22.022715545545243\n4.9,-22.135943621178654\n4.95,-22.24859546128699\n5,-22.360679774997898\n5.05,-22.47220505424423\n5.1,-22.58317958127243\n5.15,-22.693611435820436\n5.2,-22.803508501982762\n5.25,-22.9128784747792\n5.3,-23.021728866442675\n5.35,-23.130067012440755\n5.4,-23.237900077244504\n5.45,-23.345235059857504\n5.5,-23.45207879911715\n5.55,-23.55843797877949\n5.6,-23.664319132398465\n5.65,-23.769728648009426\n5.7,-23.874672772626646\n5.75,-23.979157616563597\n5.8,-24.08318915758459\n5.85,-24.186773244895647\n5.9,-24.289915602982237\n5.95,-24.392621835300936\n6,-24.49489742783178\n6.05,-24.596747752497684\n6.1,-24.698178070456937\n6.15,-24.79919353527449\n6.2,-24.899799195977465\n6.25,-25\n6.3,-25.099800796022265\n6.35,-25.199206336708304\n6.4,-25.298221281347036\n6.45,-25.39685019840059\n6.5,-25.495097567963924\n6.55,-25.592967784139454\n6.6,-25.69046515733026\n6.65,-25.787593916455258\n6.7,-25.88435821108957\n6.75,-25.98076211353316\n6.8,-26.076809620810593\n6.85,-26.1725046566048\n6.9,-26.267851073127396\n6.95,-26.362852652928133\n7,-26.457513110645905\n7.05,-26.551836094703507\n7.1,-26.645825188948457\n7.15,-26.739483914241873\n7.2,-26.832815729997478\n7.25,-26.92582403567252\n7.3,-27.018512172212592\n7.35,-27.11088342345192\n7.4,-27.202941017470884\n7.45,-27.294688127912362", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "csv_content" @@ -1346,8 +1310,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1460,12 +1423,7 @@ "preload": false, "refresh": "", "schemaVersion": 41, - "tags": [ - "gdev", - "panel-tests", - "graph-ng", - "demo" - ], + "tags": ["gdev", "panel-tests", "graph-ng", "demo"], "templating": { "list": [] }, @@ -1478,4 +1436,4 @@ "title": "Panel Tests - XY Chart Demo", "uid": "fdn48fmz8f94wc", "version": 1 -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-xychart/xychart-migrations.json b/devenv/dev-dashboards/panel-xychart/xychart-migrations.json index 326c32087b9..1399509f0e2 100644 --- a/devenv/dev-dashboards/panel-xychart/xychart-migrations.json +++ b/devenv/dev-dashboards/panel-xychart/xychart-migrations.json @@ -28,7 +28,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -97,8 +99,7 @@ { "csvContent": "X,Y\n725.1,435.6\n734.1,497.2\n714.3,527.7\n683.5,548.7\n601.8,594.0\n598.5,621.7\n573.9,644.7\n525.7,695.7\n477.2,732.8\n411.8,755.3\n353.6,758.3\n422.6,736.5\n455.3,724.1\n479.2,699.2\n474.0,673.8\n434.5,662.1\n362.2,679.8\n311.2,698.8\n260.1,728.9\n213.4,771.1\n176.2,818.0\n211.2,742.6\n253.9,707.9\n309.9,668.8\n374.7,643.2\n322.8,629.9\n277.1,607.1\n237.0,616.8\n188.9,613.9\n143.0,594.1\n101.8,566.4\n178.1,590.2\n222.2,575.9\n187.9,549.1\n161.5,517.5\n128.6,506.8\n97.3,488.3\n62.4,436.0\n99.6,473.8\n138.3,477.0\n125.0,396.7\n95.6,359.2\n83.6,322.1\n81.0,289.7\n104.0,343.8\n129.3,358.4\n151.2,291.1\n124.0,242.6\n126.3,170.2\n133.7,212.8\n148.4,243.3\n167.9,262.7\n209.1,205.7\n230.1,150.3\n231.4,120.1\n316.0,120.1\n400.6,120.1\n485.2,120.1\n569.8,120.1\n569.3,166.4\n553.0,205.5\n489.2,265.7\n422.2,309.1\n353.7,343.1\n328.2,386.3\n321.6,432.6\n334.1,473.1\n357.6,500.3\n389.9,508.5\n418.8,479.9\n447.9,413.3\n480.0,379.0\n521.6,354.2\n583.9,351.6\n549.7,357.7\n571.6,376.0\n517.1,380.8\n550.3,393.2\n504.3,402.3\n489.5,425.8\n527.5,425.8\n472.7,457.2\n447.1,523.8\n538.6,435.7\n598.4,403.7\n697.8,349.1\n645.3,390.4\n712.3,373.8\n586.0,424.1\n526.9,463.3\n469.5,538.0\n540.6,477.5\n531.2,528.4\n598.6,460.1\n594.5,509.0\n651.2,460.1\n649.9,502.6\n699.4,446.1\n707.6,477.1\n722.0,442.9", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -108,7 +109,9 @@ "type": "xychart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -234,8 +237,7 @@ { "csvFileName": "weight_height.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -246,16 +248,16 @@ { "id": "partitionByValues", "options": { - "fields": [ - "Gender" - ] + "fields": ["Gender"] } } ], "type": "xychart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -307,10 +309,7 @@ { "id": "custom.lineStyle", "value": { - "dash": [ - 10, - 15 - ], + "dash": [10, 15], "fill": "dash" } } @@ -412,8 +411,7 @@ "alias": "", "csvContent": "x,cos(x)\n-7.5,3.4663531783502584\n-7.45,3.9308257356494076\n-7.4,4.385473275743903\n-7.35,4.829159416559378\n-7.3,5.260775173811053\n-7.25,5.679241732886949\n-7.2,6.083513145322545\n-7.15,6.472578943127236\n-7.1,6.845466664428066\n-7.05,7.201244284117942\n-7,7.539022543433046\n-6.95,7.857957172636611\n-6.9,8.157251001253568\n-6.85,8.436155950581597\n-6.8,8.693974903498253\n-6.75,8.930063446890767\n-6.7,9.143831482353194\n-6.65,9.334744701125118\n-6.6,9.502325919585296\n-6.55,9.64615627196218\n-6.5,9.765876257280235\n-6.45,9.861186637925126\n-6.4,9.931849187581927\n-6.35,9.97768728667684\n-6.3,9.998586363834152\n-6.25,9.994494182244994\n-6.2,9.965420970232175\n-6.15,9.91143939568469\n-6.1,9.832684384425844\n-6.05,9.729352782968974\n-6,9.601702866503661\n-5.95,9.450053693342275\n-5.9,9.27478430744036\n-5.85,9.076332790984132\n-5.8,8.85519516941319\n-5.75,8.611924171615208\n-5.7,8.347127848391597\n-5.65,8.061468052647157\n-5.6,7.755658785102496\n-5.55,7.430464409664099\n-5.5,7.0866977429126\n-5.45,6.725218022484659\n-5.4,6.346928759426347\n-5.35,5.952775479886061\n-5.3,5.543743361791607\n-5.25,5.120854772418407\n-5.2,4.685166713003771\n-5.15,4.237768176794282\n-5.1,3.7797774271298024\n-5.05,3.3123392023675367\n-5,2.8366218546322624\n-4.95,2.353814429544512\n-4.9,1.8651236942257576\n-4.85,1.371771121009073\n-4.8,0.874989834394464\n-4.75,0.37602152887976553\n-4.7,-0.1238866346289056\n-4.65,-0.6234851460699166\n-4.6,-1.1215252693505486\n-4.55,-1.616762163536865\n-4.5,-2.107957994307797\n-4.45,-2.593885027896261\n-4.4,-3.0733286997841933\n-4.35,-3.5450906504813195\n-4.3,-4.007991720799755\n-4.25,-4.460874899137928\n-4.2,-4.902608213406994\n-4.15,-5.332087560371543\n-4.1,-5.748239465332691\n-4.05,-6.150023765255744\n-4,-6.536436208636119\n-3.95,-6.906510965605075\n-3.9,-7.259323042001402\n-3.85,-7.593990591375079\n-3.8,-7.909677119144169\n-3.75,-8.205593573395607\n-3.7,-8.48100031710408\n-3.65,-8.73520897683938\n-3.6,-8.96758416334147\n-3.55,-9.177545059662759\n-3.5,-9.364566872907963\n-3.45,-9.528182145943047\n-3.4,-9.66798192579461\n-3.35,-9.78361678581934\n-3.3,-9.87479769908865\n-3.25,-9.941296760805463\n-3.2,-9.982947757947532\n-3.15,-9.99964658471342\n-3.1,-9.991351502732794\n-3.05,-9.958083245390611\n-3,-9.899924966004454\n-2.95,-9.81702202998454\n-2.9,-9.709581651495906\n-2.85,-9.577872375530903\n-2.8,-9.42222340668658\n-2.75,-9.243023786324635\n-2.7,-9.040721420170613\n-2.65,-8.815821958782859\n-2.6,-8.568887533689473\n-2.55,-8.30053535235222\n-2.5,-8.011436155469337\n-2.45,-7.702312540473074\n-2.4,-7.373937155412454\n-2.35,-7.027130767735539\n-2.3,-6.66276021279824\n-2.25,-6.281736227227391\n-2.2,-5.885011172553458\n-2.15,-5.473576654802709\n-2.1,-5.048461045998575\n-2.05,-4.610726913767127\n-2,-4.161468365471424\n-1.95,-3.7018083135128688\n-1.9,-3.2328956686350336\n-1.85,-2.7559024682451296\n-1.8,-2.272020946930871\n-1.75,-1.7824605564949207\n-1.7,-1.2884449429552465\n-1.65,-0.7912088880673386\n-1.6,-0.29199522301288816\n-1.55,0.20794827803092428\n-1.5,0.7073720166770291\n-1.45,1.2050276936736661\n-1.4,1.6996714290024104\n-1.35,2.1900668709304147\n-1.3,2.6749882862458736\n-1.25,3.1532236239526865\n-1.2,3.623577544766736\n-1.15,4.084874408841574\n-1.1,4.5359612142557735\n-1.05,4.97571047891727\n-1,5.403023058681398\n-0.95,5.8168308946388345\n-0.9,6.216099682706644\n-0.85,6.599831458849822\n-0.8,6.967067093471654\n-0.75,7.316888688738209\n-0.7,7.648421872844885\n-0.65,7.960837985490558\n-0.6,8.253356149096783\n-0.55,8.525245220595057\n-0.5,8.775825618903728\n-0.45,9.004471023526769\n-0.4,9.210609940028851\n-0.35,9.393727128473788\n-0.3,9.55336489125606\n-0.25,9.689124217106448\n-0.2,9.800665778412416\n-0.15,9.887710779360422\n-0.1,9.950041652780257\n-0.05,9.987502603949663\n0,10\n0.05,9.987502603949663\n0.1,9.950041652780257\n0.15,9.887710779360422\n0.2,9.800665778412416\n0.25,9.689124217106448\n0.3,9.55336489125606\n0.35,9.393727128473788\n0.4,9.210609940028851\n0.45,9.004471023526769\n0.5,8.775825618903728\n0.55,8.525245220595057\n0.6,8.253356149096783\n0.65,7.960837985490558\n0.7,7.648421872844885\n0.75,7.316888688738209\n0.8,6.967067093471654\n0.85,6.599831458849822\n0.9,6.216099682706644\n0.95,5.8168308946388345\n1,5.403023058681398\n1.05,4.97571047891727\n1.1,4.5359612142557735\n1.15,4.084874408841574\n1.2,3.623577544766736\n1.25,3.1532236239526865\n1.3,2.6749882862458736\n1.35,2.1900668709304147\n1.4,1.6996714290024104\n1.45,1.2050276936736661\n1.5,0.7073720166770291\n1.55,0.20794827803092428\n1.6,-0.29199522301288816\n1.65,-0.7912088880673386\n1.7,-1.2884449429552465\n1.75,-1.7824605564949207\n1.8,-2.272020946930871\n1.85,-2.7559024682451296\n1.9,-3.2328956686350336\n1.95,-3.7018083135128688\n2,-4.161468365471424\n2.05,-4.610726913767127\n2.1,-5.048461045998575\n2.15,-5.473576654802709\n2.2,-5.885011172553458\n2.25,-6.281736227227391\n2.3,-6.66276021279824\n2.35,-7.027130767735539\n2.4,-7.373937155412454\n2.45,-7.702312540473074\n2.5,-8.011436155469337\n2.55,-8.30053535235222\n2.6,-8.568887533689473\n2.65,-8.815821958782859\n2.7,-9.040721420170613\n2.75,-9.243023786324635\n2.8,-9.42222340668658\n2.85,-9.577872375530903\n2.9,-9.709581651495906\n2.95,-9.81702202998454\n3,-9.899924966004454\n3.05,-9.958083245390611\n3.1,-9.991351502732794\n3.15,-9.99964658471342\n3.2,-9.982947757947532\n3.25,-9.941296760805463\n3.3,-9.87479769908865\n3.35,-9.78361678581934\n3.4,-9.66798192579461\n3.45,-9.528182145943047\n3.5,-9.364566872907963\n3.55,-9.177545059662759\n3.6,-8.96758416334147\n3.65,-8.73520897683938\n3.7,-8.48100031710408\n3.75,-8.205593573395607\n3.8,-7.909677119144169\n3.85,-7.593990591375079\n3.9,-7.259323042001402\n3.95,-6.906510965605075\n4,-6.536436208636119\n4.05,-6.150023765255744\n4.1,-5.748239465332691\n4.15,-5.332087560371543\n4.2,-4.902608213406994\n4.25,-4.460874899137928\n4.3,-4.007991720799755\n4.35,-3.5450906504813195\n4.4,-3.0733286997841933\n4.45,-2.593885027896261\n4.5,-2.107957994307797\n4.55,-1.616762163536865\n4.6,-1.1215252693505486\n4.65,-0.6234851460699166\n4.7,-0.1238866346289056\n4.75,0.37602152887976553\n4.8,0.874989834394464\n4.85,1.371771121009073\n4.9,1.8651236942257576\n4.95,2.353814429544512\n5,2.8366218546322624\n5.05,3.3123392023675367\n5.1,3.7797774271298024\n5.15,4.237768176794282\n5.2,4.685166713003771\n5.25,5.120854772418407\n5.3,5.543743361791607\n5.35,5.952775479886061\n5.4,6.346928759426347\n5.45,6.725218022484659\n5.5,7.0866977429126\n5.55,7.430464409664099\n5.6,7.755658785102496\n5.65,8.061468052647157\n5.7,8.347127848391597\n5.75,8.611924171615208\n5.8,8.85519516941319\n5.85,9.076332790984132\n5.9,9.27478430744036\n5.95,9.450053693342275\n6,9.601702866503661\n6.05,9.729352782968974\n6.1,9.832684384425844\n6.15,9.91143939568469\n6.2,9.965420970232175\n6.25,9.994494182244994\n6.3,9.998586363834152\n6.35,9.97768728667684\n6.4,9.931849187581927\n6.45,9.861186637925126\n6.5,9.765876257280235\n6.55,9.64615627196218\n6.6,9.502325919585296\n6.65,9.334744701125118\n6.7,9.143831482353194\n6.75,8.930063446890767\n6.8,8.693974903498253\n6.85,8.436155950581597\n6.9,8.157251001253568\n6.95,7.857957172636611\n7,7.539022543433046\n7.05,7.201244284117942\n7.1,6.845466664428066\n7.15,6.472578943127236\n7.2,6.083513145322545\n7.25,5.679241732886949\n7.3,5.260775173811053\n7.35,4.829159416559378\n7.4,4.385473275743903\n7.45,3.9308257356494076", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -421,8 +419,7 @@ { "csvContent": "x2,x^2\n-7.5,56.25\n-7.45,55.502500000000005\n-7.4,54.760000000000005\n-7.35,54.022499999999994\n-7.3,53.29\n-7.25,52.5625\n-7.2,51.84\n-7.15,51.1225\n-7.1,50.41\n-7.05,49.7025\n-7,49\n-6.95,48.3025\n-6.9,47.61000000000001\n-6.85,46.92249999999999\n-6.8,46.239999999999995\n-6.75,45.5625\n-6.7,44.89\n-6.65,44.222500000000004\n-6.6,43.559999999999995\n-6.55,42.902499999999996\n-6.5,42.25\n-6.45,41.6025\n-6.4,40.96000000000001\n-6.35,40.3225\n-6.3,39.69\n-6.25,39.0625\n-6.2,38.440000000000005\n-6.15,37.822500000000005\n-6.1,37.209999999999994\n-6.05,36.6025\n-6,36\n-5.95,35.4025\n-5.9,34.81\n-5.85,34.2225\n-5.8,33.64\n-5.75,33.0625\n-5.7,32.49\n-5.65,31.922500000000003\n-5.6,31.359999999999996\n-5.55,30.8025\n-5.5,30.25\n-5.45,29.7025\n-5.4,29.160000000000004\n-5.35,28.622499999999995\n-5.3,28.09\n-5.25,27.5625\n-5.2,27.040000000000003\n-5.15,26.522500000000004\n-5.1,26.009999999999998\n-5.05,25.502499999999998\n-5,25\n-4.95,24.5025\n-4.9,24.010000000000005\n-4.85,23.522499999999997\n-4.8,23.04\n-4.75,22.5625\n-4.7,22.090000000000003\n-4.65,21.622500000000002\n-4.6,21.159999999999997\n-4.55,20.702499999999997\n-4.5,20.25\n-4.45,19.802500000000002\n-4.4,19.360000000000003\n-4.35,18.922499999999996\n-4.3,18.49\n-4.25,18.0625\n-4.2,17.64\n-4.15,17.222500000000004\n-4.1,16.81\n-4.05,16.4025\n-4,16\n-3.95,15.602500000000001\n-3.9,15.209999999999999\n-3.85,14.822500000000002\n-3.8,14.44\n-3.75,14.0625\n-3.7,13.690000000000001\n-3.65,13.3225\n-3.6,12.96\n-3.55,12.6025\n-3.5,12.25\n-3.45,11.902500000000002\n-3.4,11.559999999999999\n-3.35,11.2225\n-3.3,10.889999999999999\n-3.25,10.5625\n-3.2,10.240000000000002\n-3.15,9.9225\n-3.1,9.610000000000001\n-3.05,9.302499999999998\n-3,9\n-2.95,8.7025\n-2.9,8.41\n-2.85,8.1225\n-2.8,7.839999999999999\n-2.75,7.5625\n-2.7,7.290000000000001\n-2.65,7.0225\n-2.6,6.760000000000001\n-2.55,6.5024999999999995\n-2.5,6.25\n-2.45,6.002500000000001\n-2.4,5.76\n-2.35,5.522500000000001\n-2.3,5.289999999999999\n-2.25,5.0625\n-2.2,4.840000000000001\n-2.15,4.6225\n-2.1,4.41\n-2.05,4.2025\n-2,4\n-1.95,3.8024999999999998\n-1.9,3.61\n-1.85,3.4225000000000003\n-1.8,3.24\n-1.75,3.0625\n-1.7,2.8899999999999997\n-1.65,2.7224999999999997\n-1.6,2.5600000000000005\n-1.55,2.4025000000000003\n-1.5,2.25\n-1.45,2.1025\n-1.4,1.9599999999999997\n-1.35,1.8225000000000002\n-1.3,1.6900000000000002\n-1.25,1.5625\n-1.2,1.44\n-1.15,1.3224999999999998\n-1.1,1.2100000000000002\n-1.05,1.1025\n-1,1\n-0.95,0.9025\n-0.9,0.81\n-0.85,0.7224999999999999\n-0.8,0.6400000000000001\n-0.75,0.5625\n-0.7,0.48999999999999994\n-0.65,0.42250000000000004\n-0.6,0.36\n-0.55,0.30250000000000005\n-0.5,0.25\n-0.45,0.2025\n-0.4,0.16000000000000003\n-0.35,0.12249999999999998\n-0.3,0.09\n-0.25,0.0625\n-0.2,0.04000000000000001\n-0.15,0.0225\n-0.1,0.010000000000000002\n-0.05,0.0025000000000000005\n0,0\n0.05,0.0025000000000000005\n0.1,0.010000000000000002\n0.15,0.0225\n0.2,0.04000000000000001\n0.25,0.0625\n0.3,0.09\n0.35,0.12249999999999998\n0.4,0.16000000000000003\n0.45,0.2025\n0.5,0.25\n0.55,0.30250000000000005\n0.6,0.36\n0.65,0.42250000000000004\n0.7,0.48999999999999994\n0.75,0.5625\n0.8,0.6400000000000001\n0.85,0.7224999999999999\n0.9,0.81\n0.95,0.9025\n1,1\n1.05,1.1025\n1.1,1.2100000000000002\n1.15,1.3224999999999998\n1.2,1.44\n1.25,1.5625\n1.3,1.6900000000000002\n1.35,1.8225000000000002\n1.4,1.9599999999999997\n1.45,2.1025\n1.5,2.25\n1.55,2.4025000000000003\n1.6,2.5600000000000005\n1.65,2.7224999999999997\n1.7,2.8899999999999997\n1.75,3.0625\n1.8,3.24\n1.85,3.4225000000000003\n1.9,3.61\n1.95,3.8024999999999998\n2,4\n2.05,4.2025\n2.1,4.41\n2.15,4.6225\n2.2,4.840000000000001\n2.25,5.0625\n2.3,5.289999999999999\n2.35,5.522500000000001\n2.4,5.76\n2.45,6.002500000000001\n2.5,6.25\n2.55,6.5024999999999995\n2.6,6.760000000000001\n2.65,7.0225\n2.7,7.290000000000001\n2.75,7.5625\n2.8,7.839999999999999\n2.85,8.1225\n2.9,8.41\n2.95,8.7025\n3,9\n3.05,9.302499999999998\n3.1,9.610000000000001\n3.15,9.9225\n3.2,10.240000000000002\n3.25,10.5625\n3.3,10.889999999999999\n3.35,11.2225\n3.4,11.559999999999999\n3.45,11.902500000000002\n3.5,12.25\n3.55,12.6025\n3.6,12.96\n3.65,13.3225\n3.7,13.690000000000001\n3.75,14.0625\n3.8,14.44\n3.85,14.822500000000002\n3.9,15.209999999999999\n3.95,15.602500000000001\n4,16\n4.05,16.4025\n4.1,16.81\n4.15,17.222500000000004\n4.2,17.64\n4.25,18.0625\n4.3,18.49\n4.35,18.922499999999996\n4.4,19.360000000000003\n4.45,19.802500000000002\n4.5,20.25\n4.55,20.702499999999997\n4.6,21.159999999999997\n4.65,21.622500000000002\n4.7,22.090000000000003\n4.75,22.5625\n4.8,23.04\n4.85,23.522499999999997\n4.9,24.010000000000005\n4.95,24.5025\n5,25\n5.05,25.502499999999998\n5.1,26.009999999999998\n5.15,26.522500000000004\n5.2,27.040000000000003\n5.25,27.5625\n5.3,28.09\n5.35,28.622499999999995\n5.4,29.160000000000004\n5.45,29.7025\n5.5,30.25\n5.55,30.8025\n5.6,31.359999999999996\n5.65,31.922500000000003\n5.7,32.49\n5.75,33.0625\n5.8,33.64\n5.85,34.2225\n5.9,34.81\n5.95,35.4025\n6,36\n6.05,36.6025\n6.1,37.209999999999994\n6.15,37.822500000000005\n6.2,38.440000000000005\n6.25,39.0625\n6.3,39.69\n6.35,40.3225\n6.4,40.96000000000001\n6.45,41.6025\n6.5,42.25\n6.55,42.902499999999996\n6.6,43.559999999999995\n6.65,44.222500000000004\n6.7,44.89\n6.75,45.5625\n6.8,46.239999999999995\n6.85,46.92249999999999\n6.9,47.61000000000001\n6.95,48.3025\n7,49\n7.05,49.7025\n7.1,50.41\n7.15,51.1225\n7.2,51.84\n7.25,52.5625\n7.3,53.29\n7.35,54.022499999999994\n7.4,54.760000000000005\n7.45,55.502500000000005", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "B", "scenarioId": "csv_content" @@ -430,8 +427,7 @@ { "csvContent": "x3,sqrt(x)\n0,0\n0.05,2.23606797749979\n0.1,3.1622776601683795\n0.15,3.872983346207417\n0.2,4.47213595499958\n0.25,5\n0.3,5.47722557505166\n0.35,5.916079783099616\n0.4,6.324555320336759\n0.45,6.708203932499369\n0.5,7.0710678118654755\n0.55,7.416198487095663\n0.6,7.745966692414834\n0.65,8.062257748298551\n0.7,8.366600265340756\n0.75,8.660254037844386\n0.8,8.94427190999916\n0.85,9.219544457292887\n0.9,9.486832980505138\n0.95,9.746794344808963\n1,10\n1.05,10.2469507659596\n1.1,10.488088481701517\n1.15,10.723805294763608\n1.2,10.95445115010332\n1.25,11.180339887498949\n1.3,11.401754250991381\n1.35,11.618950038622252\n1.4,11.832159566199232\n1.45,12.041594578792296\n1.5,12.24744871391589\n1.55,12.449899597988733\n1.6,12.649110640673518\n1.65,12.84523257866513\n1.7,13.038404810405297\n1.75,13.228756555322953\n1.8,13.416407864998739\n1.85,13.601470508735442\n1.9,13.784048752090222\n1.95,13.96424004376894\n2,14.142135623730951\n2.05,14.317821063276352\n2.1,14.49137674618944\n2.15,14.66287829861518\n2.2,14.832396974191326\n2.25,15\n2.3,15.1657508881031\n2.35,15.329709716755893\n2.4,15.491933384829668\n2.45,15.652475842498529\n2.5,15.811388300841898\n2.55,15.968719422671311\n2.6,16.124515496597102\n2.65,16.278820596099706\n2.7,16.431676725154986\n2.75,16.583123951776997\n2.8,16.73320053068151\n2.85,16.881943016134134\n2.9,17.0293863659264\n2.95,17.175564037317667\n3,17.32050807568877\n3.05,17.46424919657298\n3.1,17.60681686165901\n3.15,17.74823934929885\n3.2,17.88854381999832\n3.25,18.027756377319946\n3.3,18.16590212458495\n3.35,18.303005217723125\n3.4,18.439088914585774\n3.45,18.57417562100671\n3.5,18.708286933869708\n3.55,18.84144368141677\n3.6,18.973665961010276\n3.65,19.1049731745428\n3.7,19.235384061671347\n3.75,19.364916731037084\n3.8,19.493588689617926\n3.85,19.621416870348586\n3.9,19.748417658131498\n3.95,19.87460691435179\n4,20\n4.05,20.124611797498105\n4.1,20.248456731316583\n4.15,20.37154878746336\n4.2,20.4939015319192\n4.25,20.615528128088304\n4.3,20.73644135332772\n4.35,20.85665361461421\n4.4,20.976176963403034\n4.45,21.095023109728984\n4.5,21.213203435596423\n4.55,21.330729007701542\n4.6,21.447610589527216\n4.65,21.563858652847827\n4.7,21.6794833886788\n4.75,21.79449471770337\n4.8,21.90890230020664\n4.85,22.022715545545243\n4.9,22.135943621178654\n4.95,22.24859546128699\n5,22.360679774997898\n5.05,22.47220505424423\n5.1,22.58317958127243\n5.15,22.693611435820436\n5.2,22.803508501982762\n5.25,22.9128784747792\n5.3,23.021728866442675\n5.35,23.130067012440755\n5.4,23.237900077244504\n5.45,23.345235059857504\n5.5,23.45207879911715\n5.55,23.55843797877949\n5.6,23.664319132398465\n5.65,23.769728648009426\n5.7,23.874672772626646\n5.75,23.979157616563597\n5.8,24.08318915758459\n5.85,24.186773244895647\n5.9,24.289915602982237\n5.95,24.392621835300936\n6,24.49489742783178\n6.05,24.596747752497684\n6.1,24.698178070456937\n6.15,24.79919353527449\n6.2,24.899799195977465\n6.25,25\n6.3,25.099800796022265\n6.35,25.199206336708304\n6.4,25.298221281347036\n6.45,25.39685019840059\n6.5,25.495097567963924\n6.55,25.592967784139454\n6.6,25.69046515733026\n6.65,25.787593916455258\n6.7,25.88435821108957\n6.75,25.98076211353316\n6.8,26.076809620810593\n6.85,26.1725046566048\n6.9,26.267851073127396\n6.95,26.362852652928133\n7,26.457513110645905\n7.05,26.551836094703507\n7.1,26.645825188948457\n7.15,26.739483914241873\n7.2,26.832815729997478\n7.25,26.92582403567252\n7.3,27.018512172212592\n7.35,27.11088342345192\n7.4,27.202941017470884\n7.45,27.294688127912362", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "C", "scenarioId": "csv_content" @@ -439,8 +435,7 @@ { "csvContent": "x4,-sqrt(x)\n0,0\n0.05,-2.23606797749979\n0.1,-3.1622776601683795\n0.15,-3.872983346207417\n0.2,-4.47213595499958\n0.25,-5\n0.3,-5.47722557505166\n0.35,-5.916079783099616\n0.4,-6.324555320336759\n0.45,-6.708203932499369\n0.5,-7.0710678118654755\n0.55,-7.416198487095663\n0.6,-7.745966692414834\n0.65,-8.062257748298551\n0.7,-8.366600265340756\n0.75,-8.660254037844386\n0.8,-8.94427190999916\n0.85,-9.219544457292887\n0.9,-9.486832980505138\n0.95,-9.746794344808963\n1,-10\n1.05,-10.2469507659596\n1.1,-10.488088481701517\n1.15,-10.723805294763608\n1.2,-10.95445115010332\n1.25,-11.180339887498949\n1.3,-11.401754250991381\n1.35,-11.618950038622252\n1.4,-11.832159566199232\n1.45,-12.041594578792296\n1.5,-12.24744871391589\n1.55,-12.449899597988733\n1.6,-12.649110640673518\n1.65,-12.84523257866513\n1.7,-13.038404810405297\n1.75,-13.228756555322953\n1.8,-13.416407864998739\n1.85,-13.601470508735442\n1.9,-13.784048752090222\n1.95,-13.96424004376894\n2,-14.142135623730951\n2.05,-14.317821063276352\n2.1,-14.49137674618944\n2.15,-14.66287829861518\n2.2,-14.832396974191326\n2.25,-15\n2.3,-15.1657508881031\n2.35,-15.329709716755893\n2.4,-15.491933384829668\n2.45,-15.652475842498529\n2.5,-15.811388300841898\n2.55,-15.968719422671311\n2.6,-16.124515496597102\n2.65,-16.278820596099706\n2.7,-16.431676725154986\n2.75,-16.583123951776997\n2.8,-16.73320053068151\n2.85,-16.881943016134134\n2.9,-17.0293863659264\n2.95,-17.175564037317667\n3,-17.32050807568877\n3.05,-17.46424919657298\n3.1,-17.60681686165901\n3.15,-17.74823934929885\n3.2,-17.88854381999832\n3.25,-18.027756377319946\n3.3,-18.16590212458495\n3.35,-18.303005217723125\n3.4,-18.439088914585774\n3.45,-18.57417562100671\n3.5,-18.708286933869708\n3.55,-18.84144368141677\n3.6,-18.973665961010276\n3.65,-19.1049731745428\n3.7,-19.235384061671347\n3.75,-19.364916731037084\n3.8,-19.493588689617926\n3.85,-19.621416870348586\n3.9,-19.748417658131498\n3.95,-19.87460691435179\n4,-20\n4.05,-20.124611797498105\n4.1,-20.248456731316583\n4.15,-20.37154878746336\n4.2,-20.4939015319192\n4.25,-20.615528128088304\n4.3,-20.73644135332772\n4.35,-20.85665361461421\n4.4,-20.976176963403034\n4.45,-21.095023109728984\n4.5,-21.213203435596423\n4.55,-21.330729007701542\n4.6,-21.447610589527216\n4.65,-21.563858652847827\n4.7,-21.6794833886788\n4.75,-21.79449471770337\n4.8,-21.90890230020664\n4.85,-22.022715545545243\n4.9,-22.135943621178654\n4.95,-22.24859546128699\n5,-22.360679774997898\n5.05,-22.47220505424423\n5.1,-22.58317958127243\n5.15,-22.693611435820436\n5.2,-22.803508501982762\n5.25,-22.9128784747792\n5.3,-23.021728866442675\n5.35,-23.130067012440755\n5.4,-23.237900077244504\n5.45,-23.345235059857504\n5.5,-23.45207879911715\n5.55,-23.55843797877949\n5.6,-23.664319132398465\n5.65,-23.769728648009426\n5.7,-23.874672772626646\n5.75,-23.979157616563597\n5.8,-24.08318915758459\n5.85,-24.186773244895647\n5.9,-24.289915602982237\n5.95,-24.392621835300936\n6,-24.49489742783178\n6.05,-24.596747752497684\n6.1,-24.698178070456937\n6.15,-24.79919353527449\n6.2,-24.899799195977465\n6.25,-25\n6.3,-25.099800796022265\n6.35,-25.199206336708304\n6.4,-25.298221281347036\n6.45,-25.39685019840059\n6.5,-25.495097567963924\n6.55,-25.592967784139454\n6.6,-25.69046515733026\n6.65,-25.787593916455258\n6.7,-25.88435821108957\n6.75,-25.98076211353316\n6.8,-26.076809620810593\n6.85,-26.1725046566048\n6.9,-26.267851073127396\n6.95,-26.362852652928133\n7,-26.457513110645905\n7.05,-26.551836094703507\n7.1,-26.645825188948457\n7.15,-26.739483914241873\n7.2,-26.832815729997478\n7.25,-26.92582403567252\n7.3,-27.018512172212592\n7.35,-27.11088342345192\n7.4,-27.202941017470884\n7.45,-27.294688127912362", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "D", "scenarioId": "csv_content" @@ -450,7 +445,9 @@ "type": "xychart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "gridPos": { "h": 7, "w": 3, @@ -471,7 +468,9 @@ "type": "text" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -558,8 +557,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -569,7 +567,9 @@ "type": "xychart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -702,8 +702,7 @@ { "csvFileName": "automobiles.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -714,16 +713,16 @@ { "id": "partitionByValues", "options": { - "fields": [ - "Origin" - ] + "fields": ["Origin"] } } ], "type": "xychart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -856,8 +855,7 @@ { "csvFileName": "automobiles.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -868,9 +866,7 @@ { "id": "partitionByValues", "options": { - "fields": [ - "Origin" - ] + "fields": ["Origin"] } } ], @@ -932,9 +928,7 @@ "id": 11, "options": { "dims": { - "exclude": [ - "co TLM0100" - ], + "exclude": ["co TLM0100"], "x": "humidity TLM0100" }, "legend": { @@ -969,52 +963,20 @@ "data": { "values": [ [ - 1686898151000, - 1686898161000, - 1686898171000, - 1686898181000, - 1686898191000, - 1686898201000, - 1686898211000, - 1686898221000, - 1686898231000, - 1686898241000 + 1686898151000, 1686898161000, 1686898171000, 1686898181000, 1686898191000, 1686898201000, + 1686898211000, 1686898221000, 1686898231000, 1686898241000 ], [ - 0.4860747509084685, - 0.4790778553981873, - 0.4597786615711249, - 0.4478751241705323, - 0.44020548182152397, - 0.4569020188814074, - 0.4655250429581913, - 0.4819032762634117, - 0.491365303224968, - 0.5092239049148886 + 0.4860747509084685, 0.4790778553981873, 0.4597786615711249, 0.4478751241705323, 0.44020548182152397, + 0.4569020188814074, 0.4655250429581913, 0.4819032762634117, 0.491365303224968, 0.5092239049148886 ], [ - 35.084960222673146, - 35.089101898649055, - 35.130020139289115, - 35.10251705413486, - 35.08184755996438, - 35.04582600108574, - 35.09077388700681, - 35.04096375315356, - 35.05647864027338, - 35.091800720894916 + 35.084960222673146, 35.089101898649055, 35.130020139289115, 35.10251705413486, 35.08184755996438, + 35.04582600108574, 35.09077388700681, 35.04096375315356, 35.05647864027338, 35.091800720894916 ], [ - 71.2187151336704, - 71.24617135003214, - 71.21726829714834, - 71.26528218716341, - 71.26084702358875, - 71.24054378677393, - 71.27884802991244, - 71.29783163616926, - 71.33230999794793, - 71.34975514559837 + 71.2187151336704, 71.24617135003214, 71.21726829714834, 71.26528218716341, 71.26084702358875, + 71.24054378677393, 71.27884802991244, 71.29783163616926, 71.33230999794793, 71.34975514559837 ] ] }, @@ -1068,10 +1030,7 @@ ], "meta": { "executedQueryString": "import \"influxdata/influxdb/sample\"\nimport \"influxdata/influxdb/schema\"\n\nsample.data(set: \"airSensor\")\n |> limit(n: 10)\n |> group(columns: [\"sensor_id\"])\n |> schema.fieldsAsCols()\n", - "typeVersion": [ - 0, - 0 - ] + "typeVersion": [0, 0] }, "refId": "A" } @@ -1080,52 +1039,21 @@ "data": { "values": [ [ - 1686898151000, - 1686898161000, - 1686898171000, - 1686898181000, - 1686898191000, - 1686898201000, - 1686898211000, - 1686898221000, - 1686898231000, - 1686898241000 + 1686898151000, 1686898161000, 1686898171000, 1686898181000, 1686898191000, 1686898201000, + 1686898211000, 1686898221000, 1686898231000, 1686898241000 ], [ - 0.4964171323208575, - 0.48257922637952133, - 0.46567087322576145, - 0.4467650568596856, - 0.4417772514767169, - 0.4341379534638706, - 0.439345038257859, - 0.43452737707188627, - 0.42417500678639164, + 0.4964171323208575, 0.48257922637952133, 0.46567087322576145, 0.4467650568596856, + 0.4417772514767169, 0.4341379534638706, 0.439345038257859, 0.43452737707188627, 0.42417500678639164, 0.40704613738954887 ], [ - 34.92662088456207, - 34.91464944732493, - 34.95985887959429, - 34.929238294894844, - 34.954103486412336, - 34.98895514808448, - 35.03476240219413, - 35.028863365125844, - 35.03717014192905, - 35.00775919996651 + 34.92662088456207, 34.91464944732493, 34.95985887959429, 34.929238294894844, 34.954103486412336, + 34.98895514808448, 35.03476240219413, 35.028863365125844, 35.03717014192905, 35.00775919996651 ], [ - 71.83521495327129, - 71.83370684393908, - 71.83712160725877, - 71.88299650060345, - 71.90526594972503, - 71.8631768515712, - 71.85577028152356, - 71.89245169322045, - 71.93606971457449, - 71.96164771829956 + 71.83521495327129, 71.83370684393908, 71.83712160725877, 71.88299650060345, 71.90526594972503, + 71.8631768515712, 71.85577028152356, 71.89245169322045, 71.93606971457449, 71.96164771829956 ] ] }, @@ -1184,52 +1112,20 @@ "data": { "values": [ [ - 1686898151000, - 1686898161000, - 1686898171000, - 1686898181000, - 1686898191000, - 1686898201000, - 1686898211000, - 1686898221000, - 1686898231000, - 1686898241000 + 1686898151000, 1686898161000, 1686898171000, 1686898181000, 1686898191000, 1686898201000, + 1686898211000, 1686898221000, 1686898231000, 1686898241000 ], [ - 0.4870365157212326, - 0.5032215395360725, - 0.5035259485542879, - 0.4841798164121083, - 0.483812619615984, - 0.4882993499867786, - 0.4719009580435705, - 0.4700689455810135, - 0.48112413053169706, - 0.4691106107162724 + 0.4870365157212326, 0.5032215395360725, 0.5035259485542879, 0.4841798164121083, 0.483812619615984, + 0.4882993499867786, 0.4719009580435705, 0.4700689455810135, 0.48112413053169706, 0.4691106107162724 ], [ - 34.86359615237462, - 34.84629630959152, - 34.81771460191974, - 34.854640808896036, - 34.86699022367547, - 34.864319179513096, - 34.86915527122888, - 34.89889649251399, - 34.86576388906259, - 34.88622805723735 + 34.86359615237462, 34.84629630959152, 34.81771460191974, 34.854640808896036, 34.86699022367547, + 34.864319179513096, 34.86915527122888, 34.89889649251399, 34.86576388906259, 34.88622805723735 ], [ - 72.03972207916735, - 72.05566134520713, - 72.08473708469143, - 72.07306942754916, - 72.0427409958616, - 72.04536459775873, - 72.09400368933838, - 72.14293243585941, - 72.18840859469728, - 72.15524663568557 + 72.03972207916735, 72.05566134520713, 72.08473708469143, 72.07306942754916, 72.0427409958616, + 72.04536459775873, 72.09400368933838, 72.14293243585941, 72.18840859469728, 72.15524663568557 ] ] }, @@ -1288,52 +1184,21 @@ "data": { "values": [ [ - 1686898151000, - 1686898161000, - 1686898171000, - 1686898181000, - 1686898191000, - 1686898201000, - 1686898211000, - 1686898221000, - 1686898231000, - 1686898241000 + 1686898151000, 1686898161000, 1686898171000, 1686898181000, 1686898191000, 1686898201000, + 1686898211000, 1686898221000, 1686898231000, 1686898241000 ], [ - 0.3979338437168368, - 0.4117117830425588, - 0.4000376537804249, - 0.40114553332960723, - 0.3909759394807085, - 0.38213452854680874, - 0.37339826793170855, - 0.3624713623472977, - 0.35640734768856297, + 0.3979338437168368, 0.4117117830425588, 0.4000376537804249, 0.40114553332960723, 0.3909759394807085, + 0.38213452854680874, 0.37339826793170855, 0.3624713623472977, 0.35640734768856297, 0.35690557816119645 ], [ - 35.16192242281515, - 35.19891757080395, - 35.23754373785834, - 35.25270355198698, - 35.29525947947623, - 35.272026086051184, - 35.275233467451635, - 35.23834593104291, - 35.25233833634368, - 35.22053598631417 + 35.16192242281515, 35.19891757080395, 35.23754373785834, 35.25270355198698, 35.29525947947623, + 35.272026086051184, 35.275233467451635, 35.23834593104291, 35.25233833634368, 35.22053598631417 ], [ - 71.29169927438586, - 71.32114142326272, - 71.33632903748085, - 71.3064635464553, - 71.26436580075855, - 71.30431517798449, - 71.28861822950174, - 71.26778760430453, - 71.23481860950403, - 71.23084180913762 + 71.29169927438586, 71.32114142326272, 71.33632903748085, 71.3064635464553, 71.26436580075855, + 71.30431517798449, 71.28861822950174, 71.26778760430453, 71.23481860950403, 71.23084180913762 ] ] }, @@ -1392,52 +1257,20 @@ "data": { "values": [ [ - 1686898151000, - 1686898161000, - 1686898171000, - 1686898181000, - 1686898191000, - 1686898201000, - 1686898211000, - 1686898221000, - 1686898231000, - 1686898241000 + 1686898151000, 1686898161000, 1686898171000, 1686898181000, 1686898191000, 1686898201000, + 1686898211000, 1686898221000, 1686898231000, 1686898241000 ], [ - 0.5129015632374951, - 0.552968955740271, - 0.5690444135565038, - 0.6050221760178951, - 0.5943904374498042, - 0.6153588301241533, - 0.6164375823908576, - 0.6059635952981327, - 0.6470283318343141, - 0.6861075098169984 + 0.5129015632374951, 0.552968955740271, 0.5690444135565038, 0.6050221760178951, 0.5943904374498042, + 0.6153588301241533, 0.6164375823908576, 0.6059635952981327, 0.6470283318343141, 0.6861075098169984 ], [ - 35.75385592664215, - 35.753218714107504, - 35.72547059379115, - 35.74553237372676, - 35.73378268235891, - 35.747560005048086, - 35.769867599816735, - 35.73929899563808, - 35.705317880681875, - 35.65611547721327 + 35.75385592664215, 35.753218714107504, 35.72547059379115, 35.74553237372676, 35.73378268235891, + 35.747560005048086, 35.769867599816735, 35.73929899563808, 35.705317880681875, 35.65611547721327 ], [ - 73.64806558624832, - 73.65728412124716, - 73.68805520135102, - 73.65185281162863, - 73.63499402146681, - 73.68359308763364, - 73.64682180062367, - 73.63298265031713, - 73.59096788790947, - 73.6381949597034 + 73.64806558624832, 73.65728412124716, 73.68805520135102, 73.65185281162863, 73.63499402146681, + 73.68359308763364, 73.64682180062367, 73.63298265031713, 73.59096788790947, 73.6381949597034 ] ] }, @@ -1496,52 +1329,20 @@ "data": { "values": [ [ - 1686898151000, - 1686898161000, - 1686898171000, - 1686898181000, - 1686898191000, - 1686898201000, - 1686898211000, - 1686898221000, - 1686898231000, - 1686898241000 + 1686898151000, 1686898161000, 1686898171000, 1686898181000, 1686898191000, 1686898201000, + 1686898211000, 1686898221000, 1686898231000, 1686898241000 ], [ - 0.5082489903097206, - 0.525490999229553, - 0.5300688563597754, - 0.5351282770519872, - 0.533437724470923, - 0.5164188260958164, - 0.5134188770545406, - 0.4949993424337248, - 0.4960387472164697, - 0.5046934441184395 + 0.5082489903097206, 0.525490999229553, 0.5300688563597754, 0.5351282770519872, 0.533437724470923, + 0.5164188260958164, 0.5134188770545406, 0.4949993424337248, 0.4960387472164697, 0.5046934441184395 ], [ - 35.24972613768055, - 35.238916979727335, - 35.28758958750148, - 35.26413172906637, - 35.2875488141577, - 35.32420268624064, - 35.2932945023372, - 35.277959228995584, - 35.27901195680498, - 35.27056468599647 + 35.24972613768055, 35.238916979727335, 35.28758958750148, 35.26413172906637, 35.2875488141577, + 35.32420268624064, 35.2932945023372, 35.277959228995584, 35.27901195680498, 35.27056468599647 ], [ - 73.99618150289902, - 74.01973867729899, - 73.97488048626532, - 73.97027021897547, - 73.96055098802299, - 73.94544160099603, - 73.91010608315078, - 73.9594600253564, - 73.93323000030807, - 73.94265737589377 + 73.99618150289902, 74.01973867729899, 73.97488048626532, 73.97027021897547, 73.96055098802299, + 73.94544160099603, 73.91010608315078, 73.9594600253564, 73.93323000030807, 73.94265737589377 ] ] }, @@ -1600,52 +1401,21 @@ "data": { "values": [ [ - 1686898151000, - 1686898161000, - 1686898171000, - 1686898181000, - 1686898191000, - 1686898201000, - 1686898211000, - 1686898221000, - 1686898231000, - 1686898241000 + 1686898151000, 1686898161000, 1686898171000, 1686898181000, 1686898191000, 1686898201000, + 1686898211000, 1686898221000, 1686898231000, 1686898241000 ], [ - 0.4843506884938821, - 0.48132272563110234, - 0.4738268718199932, - 0.4929629619836042, - 0.4730642763470896, - 0.4654986284178103, - 0.47939137148137106, - 0.4729232244284411, - 0.48941836624955776, + 0.4843506884938821, 0.48132272563110234, 0.4738268718199932, 0.4929629619836042, 0.4730642763470896, + 0.4654986284178103, 0.47939137148137106, 0.4729232244284411, 0.48941836624955776, 0.49438537058971027 ], [ - 35.6843507637763, - 35.67052290561625, - 35.651191124511904, - 35.682694567957775, - 35.71754463951193, - 35.73271359197846, - 35.76201128140823, - 35.72031962293351, - 35.76508898680512, - 35.77376488207177 + 35.6843507637763, 35.67052290561625, 35.651191124511904, 35.682694567957775, 35.71754463951193, + 35.73271359197846, 35.76201128140823, 35.72031962293351, 35.76508898680512, 35.77376488207177 ], [ - 75.28536533620796, - 75.3203884403274, - 75.32915092826639, - 75.3260291876276, - 75.29613126188146, - 75.31098782530198, - 75.27434784939884, - 75.30929861509146, - 75.32818662869892, - 75.33650169518009 + 75.28536533620796, 75.3203884403274, 75.32915092826639, 75.3260291876276, 75.29613126188146, + 75.31098782530198, 75.27434784939884, 75.30929861509146, 75.32818662869892, 75.33650169518009 ] ] }, @@ -1704,52 +1474,20 @@ "data": { "values": [ [ - 1686898151000, - 1686898161000, - 1686898171000, - 1686898181000, - 1686898191000, - 1686898201000, - 1686898211000, - 1686898221000, - 1686898231000, - 1686898241000 + 1686898151000, 1686898161000, 1686898171000, 1686898181000, 1686898191000, 1686898201000, + 1686898211000, 1686898221000, 1686898231000, 1686898241000 ], [ - 0.39373421338928505, - 0.40929905142175416, - 0.39727569840202215, - 0.415834035946844, - 0.413865826090914, - 0.4271278288285897, - 0.435277474574255, - 0.4480042029285831, - 0.462641109361364, - 0.4566678920783633 + 0.39373421338928505, 0.40929905142175416, 0.39727569840202215, 0.415834035946844, 0.413865826090914, + 0.4271278288285897, 0.435277474574255, 0.4480042029285831, 0.462641109361364, 0.4566678920783633 ], [ - 35.86322585633077, - 35.88732568460068, - 35.87553827435961, - 35.84063967239316, - 35.813642046057005, - 35.8569165461967, - 35.80924651587166, - 35.80599199551315, - 35.75906423980926, - 35.79948361256034 + 35.86322585633077, 35.88732568460068, 35.87553827435961, 35.84063967239316, 35.813642046057005, + 35.8569165461967, 35.80924651587166, 35.80599199551315, 35.75906423980926, 35.79948361256034 ], [ - 74.78112365229704, - 74.73821283157658, - 74.73019922701285, - 74.73280967321202, - 74.74885460260536, - 74.70880911705714, - 74.73346687605624, - 74.77682413662681, - 74.80562351134131, - 74.82881664528719 + 74.78112365229704, 74.73821283157658, 74.73019922701285, 74.73280967321202, 74.74885460260536, + 74.70880911705714, 74.73346687605624, 74.77682413662681, 74.80562351134131, 74.82881664528719 ] ] }, @@ -1811,7 +1549,9 @@ "type": "xychart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1898,8 +1638,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -1909,7 +1648,9 @@ "type": "xychart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -1996,8 +1737,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -2007,7 +1747,9 @@ "type": "xychart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -2100,8 +1842,7 @@ { "csvFileName": "flight_info_by_state.csv", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_file" @@ -2113,11 +1854,7 @@ ], "refresh": "", "schemaVersion": 39, - "tags": [ - "gdev", - "panel-tests", - "graph-ng" - ], + "tags": ["gdev", "panel-tests", "graph-ng"], "templating": { "list": [] }, @@ -2131,4 +1868,4 @@ "uid": "YObbMr44k", "version": 1, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/panel-xychart/xychart-tooltip-color-test.json b/devenv/dev-dashboards/panel-xychart/xychart-tooltip-color-test.json index e92699d02e3..b0ae2a9d76b 100644 --- a/devenv/dev-dashboards/panel-xychart/xychart-tooltip-color-test.json +++ b/devenv/dev-dashboards/panel-xychart/xychart-tooltip-color-test.json @@ -100,8 +100,7 @@ { "csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "${DS_GDEV-TESTDATA}" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -189,8 +188,7 @@ { "csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "${DS_GDEV-TESTDATA}" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -278,8 +276,7 @@ { "csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "${DS_GDEV-TESTDATA}" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -374,8 +371,7 @@ { "csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "${DS_GDEV-TESTDATA}" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -470,8 +466,7 @@ { "csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "${DS_GDEV-TESTDATA}" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -566,8 +561,7 @@ { "csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "${DS_GDEV-TESTDATA}" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -577,7 +571,9 @@ "type": "xychart" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -654,8 +650,7 @@ { "csvContent": "x,y,c\n0,0,0\n50,50,25\n100,100,50", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -681,4 +676,4 @@ "uid": "cb67db43-dd72-4ada-a313-53f46c20adcc", "version": 1, "weekStart": "" -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/scenarios/relative_time_zone_support.json b/devenv/dev-dashboards/scenarios/relative_time_zone_support.json index 5fcf0e10597..e50ababea25 100644 --- a/devenv/dev-dashboards/scenarios/relative_time_zone_support.json +++ b/devenv/dev-dashboards/scenarios/relative_time_zone_support.json @@ -24,8 +24,7 @@ "panels": [ { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -68,9 +67,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -79,8 +76,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -92,8 +88,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -136,9 +131,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -147,8 +140,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", diff --git a/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json b/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json index 8966137b55e..a62b9452a65 100644 --- a/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json +++ b/devenv/dev-dashboards/scenarios/slow_queries_and_annotations.json @@ -34,7 +34,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 7, @@ -118,7 +120,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 7, @@ -202,7 +206,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 7, @@ -286,7 +292,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 7, @@ -370,7 +378,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 7, @@ -454,7 +464,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 5, @@ -538,7 +550,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 5, @@ -622,7 +636,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 5, @@ -706,7 +722,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 5, @@ -790,7 +808,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 5, @@ -874,7 +894,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 5, @@ -958,7 +980,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 6, @@ -1042,7 +1066,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "fill": 1, "gridPos": { "h": 6, diff --git a/devenv/dev-dashboards/scenarios/time_zone_support.json b/devenv/dev-dashboards/scenarios/time_zone_support.json index cc1f81ee221..23b3fc020f7 100644 --- a/devenv/dev-dashboards/scenarios/time_zone_support.json +++ b/devenv/dev-dashboards/scenarios/time_zone_support.json @@ -23,7 +23,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fieldConfig": { @@ -121,7 +123,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fieldConfig": { @@ -219,7 +223,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fieldConfig": { @@ -328,7 +334,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fieldConfig": { @@ -448,7 +456,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "editable": true, "error": false, "fieldConfig": { @@ -561,7 +571,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "gdev-testdata", + "datasource": { + "type": "grafana-testdata-datasource" + }, "decimals": 3, "fieldConfig": { "defaults": { diff --git a/devenv/dev-dashboards/transforms/config-from-query.json b/devenv/dev-dashboards/transforms/config-from-query.json index cb65f966667..c572e80c438 100644 --- a/devenv/dev-dashboards/transforms/config-from-query.json +++ b/devenv/dev-dashboards/transforms/config-from-query.json @@ -29,8 +29,7 @@ "panels": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -109,8 +108,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "max": 100, @@ -123,8 +121,7 @@ "alias": "", "csvContent": "min,max,threshold1\n1000,1000,8000\n0,100,80\n\n", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "config", "scenarioId": "csv_content" @@ -144,8 +141,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -203,9 +199,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -215,8 +209,7 @@ { "csvContent": "Name, Value, SensorA, MyUnit, MyColor\nGoogle, 10, 50, km/h, blue\nGoogle, 100, 100,km/h, orange\n", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "A", @@ -252,8 +245,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -312,9 +304,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -324,8 +314,7 @@ { "csvContent": "ID, DisplayText\n21412312312, Homer\n12421412413, Simpsons \n12321312313, Bart", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "A", @@ -356,8 +345,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -416,9 +404,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -428,8 +414,7 @@ { "csvContent": "Value, Color\nOK, blue\nPretty bad, red\nYay it's green, green\nSomething is off, orange\nNo idea, #88AA00\nAm I purple?, purple", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "A", @@ -464,8 +449,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -508,9 +492,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -520,8 +502,7 @@ { "csvContent": "ID, Value\n21412312312, 100\n12421412413, 20\n12321312313, 10", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "A", @@ -552,8 +533,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -633,8 +613,7 @@ { "csvContent": "ID, Value\nA21412312312, 100\nA12421412413, 20\nA12321312313, 10\n", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "data", @@ -643,8 +622,7 @@ { "csvContent": "ID, DisplayText\nA21412312312, Homer\nA12421412413, Marge \nA12321312313, Bart", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "mappings", @@ -679,9 +657,7 @@ }, { "datasource": { - "default": true, - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -757,8 +733,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"typeVersion\": [\n 0,\n 0\n ],\n \"custom\": {\n \"customStat\": 10\n }\n },\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {\n \"interval\": 300000\n }\n },\n {\n \"name\": \"A-series\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"labels\": {},\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1732120345536,\n 1732120645536,\n 1732120945536,\n 1732121245536,\n 1732121545536,\n 1732121845536,\n 1732122145536,\n 1732122445536,\n 1732122745536,\n 1732123045536,\n 1732123345536,\n 1732123645536,\n 1732123945536,\n 1732124245536,\n 1732124545536,\n 1732124845536,\n 1732125145536,\n 1732125445536,\n 1732125745536,\n 1732126045536,\n 1732126345536,\n 1732126645536,\n 1732126945536,\n 1732127245536,\n 1732127545536,\n 1732127845536,\n 1732128145536,\n 1732128445536,\n 1732128745536,\n 1732129045536,\n 1732129345536,\n 1732129645536,\n 1732129945536,\n 1732130245536,\n 1732130545536,\n 1732130845536,\n 1732131145536,\n 1732131445536,\n 1732131745536,\n 1732132045536,\n 1732132345536,\n 1732132645536,\n 1732132945536,\n 1732133245536,\n 1732133545536,\n 1732133845536,\n 1732134145536,\n 1732134445536,\n 1732134745536,\n 1732135045536,\n 1732135345536,\n 1732135645536,\n 1732135945536,\n 1732136245536,\n 1732136545536,\n 1732136845536,\n 1732137145536,\n 1732137445536,\n 1732137745536,\n 1732138045536,\n 1732138345536,\n 1732138645536,\n 1732138945536,\n 1732139245536,\n 1732139545536,\n 1732139845536,\n 1732140145536,\n 1732140445536,\n 1732140745536,\n 1732141045536,\n 1732141345536,\n 1732141645536\n ],\n [\n 36.67835770082578,\n 35.674537924065,\n 8.339763723800829,\n 16.313291374141446,\n 66.05915891584247,\n 55.975417240601566,\n 33.75563648171818,\n 10.561077849025175,\n 20.31936089572975,\n 26.11219409670694,\n 57.542750561307564,\n 67.10954340535248,\n 82.95323961635275,\n 100.9691805551439,\n 59.829706792214644,\n 94.58723331927925,\n 89.3082374466047,\n 58.69065135820439,\n 97.144192150251,\n 139.99199318295675,\n 157.9473973408396,\n 177.94452058033198,\n 188.84065573954362,\n 154.3930906887033,\n 130.14406878049226,\n 116.65818233680316,\n 100.96041794526472,\n 144.65142921584447,\n 175.75178611497054,\n 203.55271609883386,\n 238.4931714915047,\n 253.38754249911452,\n 271.1735238723396,\n 258.54418620287515,\n 260.8463123020904,\n 216.10614084307323,\n 253.30389406688175,\n 249.37108721413884,\n 243.7226799137106,\n 216.74579233434042,\n 262.50043010512826,\n 238.71564300219498,\n 218.3552317737898,\n 195.6154411937393,\n 154.1987522722987,\n 124.00066408416897,\n 146.6474694384778,\n 101.68405646311294,\n 104.5791139459948,\n 85.39428966503652,\n 78.45166775446714,\n 56.285707917841535,\n 36.22861441808941,\n 35.098428846082555,\n 68.67835646605371,\n 101.67142528391042,\n 151.04038339587296,\n 114.77414457402928,\n 72.65341528313934,\n 113.42643748928826,\n 151.09282092262364,\n 163.24422498859587,\n 183.86606816236363,\n 230.24678524811478,\n 205.94887669562561,\n 211.24387656976373,\n 217.26738326873522,\n 214.66898480692646,\n 206.95531499977153,\n 194.19724584765092,\n 146.16071387746757,\n 188.30193538777615\n ]\n ]\n }\n }\n]", "refId": "A", @@ -790,10 +765,7 @@ ], "refresh": "", "schemaVersion": 40, - "tags": [ - "gdev", - "transform" - ], + "tags": ["gdev", "transform"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/transforms/extract-json-paths.json b/devenv/dev-dashboards/transforms/extract-json-paths.json index a9141cc7f4d..337ae2c8fbd 100644 --- a/devenv/dev-dashboards/transforms/extract-json-paths.json +++ b/devenv/dev-dashboards/transforms/extract-json-paths.json @@ -14,13 +14,7 @@ "target": { "limit": 100, "matchAny": false, - "tags": [ - "gdev", - "transform", - "transformations", - "extract", - "json" - ], + "tags": ["gdev", "transform", "transformations", "extract", "json"], "type": "dashboard" }, "type": "dashboard" @@ -66,8 +60,7 @@ } ], "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "pluginVersion": "9.4.0-pre", "fieldConfig": { @@ -100,9 +93,7 @@ "showHeader": true, "footer": { "show": false, - "reducer": [ - "sum" - ], + "reducer": ["sum"], "countRows": false, "fields": "" } @@ -110,8 +101,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[{\"schema\":{\"refId\":\"A\",\"meta\":{\"channel\":\"ds/bHGPS1h4z/1s/test\",\"transformations\":[\"extractFields\",\"extractFields\",\"extractFields\"]},\"fields\":[{\"name\":\"Time\",\"type\":\"time\",\"config\":{\"custom\":{\"align\":\"auto\",\"displayMode\":\"auto\",\"inspect\":false},\"color\":{\"mode\":\"thresholds\"},\"thresholds\":{\"mode\":\"absolute\",\"steps\":[{\"color\":\"green\",\"value\":null},{\"color\":\"red\",\"value\":80}]}}},{\"name\":\"Value\",\"type\":\"other\",\"config\":{\"custom\":{\"align\":\"auto\",\"displayMode\":\"auto\",\"inspect\":false},\"color\":{\"mode\":\"thresholds\"},\"mappings\":[],\"thresholds\":{\"mode\":\"absolute\",\"steps\":[{\"color\":\"green\",\"value\":null},{\"color\":\"red\",\"value\":80}]}}}]},\"data\":{\"values\":[[1673543683471,1673543689063,1673543695050],[[\"2023-01-12T17:14:44.419Z\",62,141,79,29,79,-29,29,{\"testdata\":{\"source1\":{\"value1\":9,\"value2\":18},\"source2\":[[0,1,2,3,4,5,6,7,8,9],[7,11,13,17,19,23,27,29]]}}],[\"2023-01-12T17:14:50.050Z\",62,143,81,29,81,-29,29,{\"testdata\":{\"source1\":{\"value1\":10,\"value2\":20},\"source2\":[[1,2,3,4,5,6,7,8,9,10],[11,13,17,19,23,27,29,31]]}}],[\"2023-01-12T17:14:55.050Z\",61,146,80,22,85,-28,28,{\"testdata\":{\"source1\":{\"value1\":11,\"value2\":22},\"source2\":[[3,4,5,6,7,8,9,10,11,12],[13,17,19,23,27,29,31,37,41]]}}]]]}}]", "refId": "A", @@ -148,8 +138,7 @@ } ], "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "pluginVersion": "9.4.0-pre", "fieldConfig": { @@ -219,8 +208,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[{\"schema\":{\"refId\":\"A\",\"meta\":{\"channel\":\"ds/bHGPS1h4z/1s/test\",\"transformations\":[\"extractFields\",\"extractFields\",\"extractFields\"]},\"fields\":[{\"name\":\"Time\",\"type\":\"time\",\"config\":{\"custom\":{\"align\":\"auto\",\"displayMode\":\"auto\",\"inspect\":false},\"color\":{\"mode\":\"thresholds\"},\"thresholds\":{\"mode\":\"absolute\",\"steps\":[{\"color\":\"green\",\"value\":null},{\"color\":\"red\",\"value\":80}]}}},{\"name\":\"Value\",\"type\":\"other\",\"config\":{\"custom\":{\"align\":\"auto\",\"displayMode\":\"auto\",\"inspect\":false},\"color\":{\"mode\":\"thresholds\"},\"mappings\":[],\"thresholds\":{\"mode\":\"absolute\",\"steps\":[{\"color\":\"green\",\"value\":null},{\"color\":\"red\",\"value\":80}]}}}]},\"data\":{\"values\":[[1673543683471,1673543689063,1673543695050],[[\"2023-01-12T17:14:44.419Z\",62,141,79,29,79,-29,29,{\"testdata\":{\"source1\":{\"value1\":9,\"value2\":18},\"source2\":[[0,1,2,3,4,5,6,7,8,9],[7,11,13,17,19,23,27,29]]}}],[\"2023-01-12T17:14:50.050Z\",62,143,81,29,81,-29,29,{\"testdata\":{\"source1\":{\"value1\":10,\"value2\":20},\"source2\":[[1,2,3,4,5,6,7,8,9,10],[11,13,17,19,23,27,29,31]]}}],[\"2023-01-12T17:14:55.050Z\",61,146,80,22,85,-28,28,{\"testdata\":{\"source1\":{\"value1\":11,\"value2\":22},\"source2\":[[3,4,5,6,7,8,9,10,11,12],[13,17,19,23,27,29,31,37,41]]}}]]]}}]", "refId": "A", @@ -241,8 +229,7 @@ "title": "Raw data", "transformations": [], "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "pluginVersion": "9.4.0-pre", "fieldConfig": { @@ -275,9 +262,7 @@ "showHeader": true, "footer": { "show": false, - "reducer": [ - "sum" - ], + "reducer": ["sum"], "countRows": false, "fields": "" } @@ -285,8 +270,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "gdev-testdata" + "type": "grafana-testdata-datasource" }, "rawFrameContent": "[{\"schema\":{\"refId\":\"A\",\"meta\":{\"channel\":\"ds/bHGPS1h4z/1s/test\",\"transformations\":[\"extractFields\",\"extractFields\",\"extractFields\"]},\"fields\":[{\"name\":\"Time\",\"type\":\"time\",\"config\":{\"custom\":{\"align\":\"auto\",\"displayMode\":\"auto\",\"inspect\":false},\"color\":{\"mode\":\"thresholds\"},\"thresholds\":{\"mode\":\"absolute\",\"steps\":[{\"color\":\"green\",\"value\":null},{\"color\":\"red\",\"value\":80}]}}},{\"name\":\"Value\",\"type\":\"other\",\"config\":{\"custom\":{\"align\":\"auto\",\"displayMode\":\"auto\",\"inspect\":false},\"color\":{\"mode\":\"thresholds\"},\"mappings\":[],\"thresholds\":{\"mode\":\"absolute\",\"steps\":[{\"color\":\"green\",\"value\":null},{\"color\":\"red\",\"value\":80}]}}}]},\"data\":{\"values\":[[1673543683471,1673543689063,1673543695050],[[\"2023-01-12T17:14:44.419Z\",62,141,79,29,79,-29,29,{\"testdata\":{\"source1\":{\"value1\":9,\"value2\":18},\"source2\":[[0,1,2,3,4,5,6,7,8,9],[7,11,13,17,19,23,27,29]]}}],[\"2023-01-12T17:14:50.050Z\",62,143,81,29,81,-29,29,{\"testdata\":{\"source1\":{\"value1\":10,\"value2\":20},\"source2\":[[1,2,3,4,5,6,7,8,9,10],[11,13,17,19,23,27,29,31]]}}],[\"2023-01-12T17:14:55.050Z\",61,146,80,22,85,-28,28,{\"testdata\":{\"source1\":{\"value1\":11,\"value2\":22},\"source2\":[[3,4,5,6,7,8,9,10,11,12],[13,17,19,23,27,29,31,37,41]]}}]]]}}]", "refId": "A", @@ -298,10 +282,7 @@ ], "revision": 1, "schemaVersion": 37, - "tags": [ - "gdev", - "transform" - ], + "tags": ["gdev", "transform"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/transforms/filter.json b/devenv/dev-dashboards/transforms/filter.json index e06a0520a14..d1d38914f03 100644 --- a/devenv/dev-dashboards/transforms/filter.json +++ b/devenv/dev-dashboards/transforms/filter.json @@ -1,170 +1,158 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 1394, - "links": [], - "liveNow": false, - "panels": [ + "annotations": { + "list": [ { + "builtIn": 1, "datasource": { - "uid": "PD8C576611E62080A", - "type": "testdata" + "type": "grafana", + "uid": "-- Grafana --" }, - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "color": { - "mode": "thresholds" - } - }, - "overrides": [] + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" }, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 2, - "targets": [ - { - "scenarioId": "csv_content", - "refId": "A", - "datasource": { - "uid": "PD8C576611E62080A", - "type": "testdata" - }, - "csvContent": "AAA\n1\n2\n3\n4" - }, - { - "scenarioId": "csv_content", - "refId": "B", - "datasource": { - "uid": "PD8C576611E62080A", - "type": "testdata" - }, - "csvContent": "BBB\n1\n2\n3\n4\n", - "hide": false - } - ], - "title": "Transformer query filters", - "type": "table", - "transformations": [ - { - "id": "reduce", - "options": { - "reducers": [ - "min" - ], - "mode": "reduceFields", - "includeTimeField": false - }, - "filter": { - "id": "byRefId", - "options": "A" - } - }, - { - "id": "reduce", - "options": { - "reducers": [ - "max" - ], - "mode": "reduceFields", - "includeTimeField": false - }, - "filter": { - "id": "byRefId", - "options": "B" - } - }, - { - "id": "concatenate", - "options": {} - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": {}, - "renameByName": { - "AAA": "Min from Query A", - "BBB": "Max from Query B" - } - } - } - ], - "options": { - "showHeader": true, - "footer": { - "show": false, - "reducer": [ - "sum" - ], - "countRows": false, - "fields": "" - }, - "frameIndex": 0 - }, - "pluginVersion": "9.4.0-pre" + "type": "dashboard" } - ], - "schemaVersion": 37, - "tags": [ - "gdev", - "transform" - ], - "templating": { - "list": [] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Transforms - Filters", - "uid": "fGWBVW4k" - } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1394, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "color": { + "mode": "thresholds" + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 2, + "targets": [ + { + "scenarioId": "csv_content", + "refId": "A", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "csvContent": "AAA\n1\n2\n3\n4" + }, + { + "scenarioId": "csv_content", + "refId": "B", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "csvContent": "BBB\n1\n2\n3\n4\n", + "hide": false + } + ], + "title": "Transformer query filters", + "type": "table", + "transformations": [ + { + "id": "reduce", + "options": { + "reducers": ["min"], + "mode": "reduceFields", + "includeTimeField": false + }, + "filter": { + "id": "byRefId", + "options": "A" + } + }, + { + "id": "reduce", + "options": { + "reducers": ["max"], + "mode": "reduceFields", + "includeTimeField": false + }, + "filter": { + "id": "byRefId", + "options": "B" + } + }, + { + "id": "concatenate", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "AAA": "Min from Query A", + "BBB": "Max from Query B" + } + } + } + ], + "options": { + "showHeader": true, + "footer": { + "show": false, + "reducer": ["sum"], + "countRows": false, + "fields": "" + }, + "frameIndex": 0 + }, + "pluginVersion": "9.4.0-pre" + } + ], + "schemaVersion": 37, + "tags": ["gdev", "transform"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Transforms - Filters", + "uid": "fGWBVW4k" +} diff --git a/devenv/dev-dashboards/transforms/join-by-field.json b/devenv/dev-dashboards/transforms/join-by-field.json index 2c254fc929e..f1a98a4b8d8 100644 --- a/devenv/dev-dashboards/transforms/join-by-field.json +++ b/devenv/dev-dashboards/transforms/join-by-field.json @@ -42,7 +42,9 @@ "type": "row" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -116,8 +118,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk", @@ -168,9 +169,7 @@ "options": { "footer": { "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -230,9 +229,7 @@ "options": { "footer": { "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -271,7 +268,9 @@ "type": "row" }, { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -308,9 +307,7 @@ "options": { "footer": { "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "frameIndex": 0, @@ -321,8 +318,7 @@ { "csvContent": "OrderID,CustomerID,Time\n100,A,10000\n101,B,20000\n102,C,30000", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "Orders", "scenarioId": "csv_content" @@ -330,8 +326,7 @@ { "csvContent": "CustomerID,Name,Country\nA,Customer A,USA\nB,Customer B,Germany\nC,Customer C,Spain\nD,Customer D,Canada", "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "refId": "Customers", @@ -383,9 +378,7 @@ "options": { "footer": { "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "frameIndex": 1, @@ -472,9 +465,7 @@ "options": { "footer": { "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "frameIndex": 0, @@ -570,9 +561,7 @@ "options": { "footer": { "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "frameIndex": 0, @@ -604,10 +593,7 @@ } ], "schemaVersion": 37, - "tags": [ - "gdev", - "transform" - ], + "tags": ["gdev", "transform"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/transforms/join-by-labels.json b/devenv/dev-dashboards/transforms/join-by-labels.json index 7b546b58c80..0a99497b823 100644 --- a/devenv/dev-dashboards/transforms/join-by-labels.json +++ b/devenv/dev-dashboards/transforms/join-by-labels.json @@ -29,7 +29,9 @@ "liveNow": false, "panels": [ { - "datasource": { "type": "testdata" }, + "datasource": { + "type": "grafana-testdata-datasource" + }, "fieldConfig": { "defaults": { "color": { @@ -65,9 +67,7 @@ "justifyMode": "auto", "orientation": "auto", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -77,8 +77,7 @@ "targets": [ { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "labels": "site=A,measure=speed,state=CA", "refId": "A", @@ -86,8 +85,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "labels": "site=B,measure=speed,state=OR", @@ -96,8 +94,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "labels": "site=B,measure=temp", @@ -106,8 +103,7 @@ }, { "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "hide": false, "labels": "site=A,measure=temp", @@ -160,9 +156,7 @@ "options": { "footer": { "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "frameIndex": 2, @@ -225,9 +219,7 @@ "options": { "footer": { "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -248,9 +240,7 @@ { "id": "joinByLabels", "options": { - "join": [ - "site" - ], + "join": ["site"], "value": "measure" } } @@ -299,9 +289,7 @@ "options": { "footer": { "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true @@ -330,10 +318,7 @@ } ], "schemaVersion": 37, - "tags": [ - "gdev", - "transform" - ], + "tags": ["gdev", "transform"], "templating": { "list": [] }, diff --git a/devenv/dev-dashboards/transforms/regression-analysis.json b/devenv/dev-dashboards/transforms/regression-analysis.json index f9330ff55cf..dc48d3fd498 100644 --- a/devenv/dev-dashboards/transforms/regression-analysis.json +++ b/devenv/dev-dashboards/transforms/regression-analysis.json @@ -24,8 +24,7 @@ "panels": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -106,8 +105,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" @@ -132,8 +130,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -214,8 +211,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "random_walk" @@ -239,8 +235,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -322,8 +317,7 @@ { "csvContent": "time, val\n2023-11-20 12:09:00, 1\n2023-11-20 12:09:02, 2\n2023-11-20 12:09:03, 3\n2023-11-20 12:09:04, 4\n2023-11-20 12:09:05, 5\n2023-11-20 12:09:06, 6\n2023-11-20 12:09:07, 2\n2023-11-20 12:09:08, 3\n2023-11-20 12:09:09, 4\n2023-11-20 12:09:10, 1\n2023-11-20 12:09:11, 2\n2023-11-20 12:09:12, 3\n2023-11-20 12:09:13, 4", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -368,8 +362,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -451,8 +444,7 @@ { "csvContent": "time, val\n2023-11-20 12:09:00, 1\n2023-11-20 12:09:02, 2\n2023-11-20 12:09:03, 3\n2023-11-20 12:09:04, 4\n2023-11-20 12:09:05, null\n2023-11-20 12:09:06, 6\n2023-11-20 12:09:07, 2\n2023-11-20 12:09:08, null\n2023-11-20 12:09:09, 4\n2023-11-20 12:09:10, 1\n2023-11-20 12:09:11, 2\n2023-11-20 12:09:12, 3\n2023-11-20 12:09:13, 4", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -498,8 +490,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -618,8 +609,7 @@ { "csvContent": "x,y\n1,4\n2,1\n3,2\n4,-2\n5,6\n3,2\n1,7\n3,9\n6,3\n5,-3\n2,-2\n7,15", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -676,8 +666,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -715,9 +704,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -756,8 +743,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -864,8 +850,7 @@ { "csvContent": "x, val\n6,2\n8,1\n10,5\n15,1\n22,10\n", "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "csv_content" @@ -909,8 +894,7 @@ }, { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { @@ -1028,8 +1012,7 @@ "targets": [ { "datasource": { - "type": "grafana-testdata-datasource", - "uid": "PD8C576611E62080A" + "type": "grafana-testdata-datasource" }, "refId": "A", "scenarioId": "usa", @@ -1072,10 +1055,7 @@ "preload": false, "refresh": "", "schemaVersion": 41, - "tags": [ - "gdev", - "transform" - ], + "tags": ["gdev", "transform"], "templating": { "list": [] }, @@ -1088,4 +1068,4 @@ "title": "Transforms - Regression analysis", "uid": "d2d2bb99-42e4-44b8-b93e-3ad1aae31c6b", "version": 1 -} \ No newline at end of file +} diff --git a/devenv/dev-dashboards/transforms/reuse.json b/devenv/dev-dashboards/transforms/reuse.json index 8a59840c9a8..f207ab6c9fc 100644 --- a/devenv/dev-dashboards/transforms/reuse.json +++ b/devenv/dev-dashboards/transforms/reuse.json @@ -1,538 +1,525 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 1394, - "links": [], - "liveNow": false, - "panels": [ - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" }, - "gridPos": { - "h": 3, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 9, - "options": { - "content": "Dashboard queries allow re-using the same results from one panel in another panel context.\n\nThis dashboard shows a single panel that makes a real query and applies transformations. The other panels, all use the same results rather than make their own query requests.", - "mode": "markdown" - }, - "pluginVersion": "9.2.0-pre", - "type": "text" - }, - { - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 18, - "w": 7, - "x": 0, - "y": 3 - }, - "id": 2, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "9.2.0-pre", - "targets": [ - { - "csvFileName": "flight_info_by_state.csv", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "A", - "scenarioId": "csv_file" - }, - { - "csvFileName": "population_by_state.csv", - "datasource": { - "type": "testdata", - "uid": "PD8C576611E62080A" - }, - "refId": "B", - "scenarioId": "csv_file" - } - ], - "title": "Raw data -- with outer join", - "transformations": [ - { - "id": "seriesToColumns", - "options": { - "byField": "State", - "mode": "outer" - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "1980 population_by_state.csv": true, - "2000 population_by_state.csv": true, - "DestLocation flight_info_by_state.csv": true, - "Lat flight_info_by_state.csv": true, - "Lng flight_info_by_state.csv": true, - "Price flight_info_by_state.csv": true - }, - "indexByName": {}, - "renameByName": { - "2020 population_by_state.csv": "2020 population", - "Count flight_info_by_state.csv": "Flight count", - "Price flight_info_by_state.csv": "" - } - } - } - ], - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 11, - "x": 7, - "y": 3 - }, - "id": 4, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "9.2.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A" - } - ], - "title": "Reused data (without transform)", - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 6, - "x": 18, - "y": 3 - }, - "id": 5, - "options": { - "basemap": { - "config": {}, - "name": "Layer 0", - "type": "default" - }, - "controls": { - "mouseWheelZoom": true, - "showAttribution": true, - "showDebug": false, - "showMeasure": false, - "showScale": false, - "showZoom": true - }, - "layers": [ - { - "config": { - "showLegend": true, - "style": { - "color": { - "fixed": "dark-green" - }, - "opacity": 0.4, - "rotation": { - "fixed": 0, - "max": 360, - "min": -360, - "mode": "mod" - }, - "size": { - "field": "Flight count", - "fixed": 5, - "max": 15, - "min": 2 - }, - "symbol": { - "fixed": "img/icons/marker/circle.svg", - "mode": "fixed" - }, - "textConfig": { - "fontSize": 12, - "offsetX": 0, - "offsetY": 0, - "textAlign": "center", - "textBaseline": "middle" - } - } - }, - "location": { - "gazetteer": "public/gazetteer/usa-states.json", - "lookup": "State", - "mode": "lookup" - }, - "name": "Flight count", - "tooltip": true, - "type": "markers" - } - ], - "tooltip": { - "mode": "details" - }, - "view": { - "id": "coords", - "lat": 35.70008, - "lon": -93.558296, - "zoom": 3.09 - } - }, - "pluginVersion": "9.2.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A", - "withTransforms": true - } - ], - "title": "Reused data (without transform)", - "type": "geomap" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "displayMode": "auto", - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 11, - "x": 7, - "y": 12 - }, - "id": 6, - "options": { - "footer": { - "fields": "", - "reducer": [ - "sum" - ], - "show": false - }, - "showHeader": true - }, - "pluginVersion": "9.2.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A", - "withTransforms": true - } - ], - "title": "Reused data (with transform)", - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 9, - "w": 6, - "x": 18, - "y": 12 - }, - "id": 7, - "options": { - "basemap": { - "config": {}, - "name": "Layer 0", - "type": "default" - }, - "controls": { - "mouseWheelZoom": true, - "showAttribution": true, - "showDebug": false, - "showMeasure": false, - "showScale": false, - "showZoom": true - }, - "layers": [ - { - "config": { - "showLegend": true, - "style": { - "color": { - "fixed": "dark-green" - }, - "opacity": 0.4, - "rotation": { - "fixed": 0, - "max": 360, - "min": -360, - "mode": "mod" - }, - "size": { - "field": "2020 population", - "fixed": 5, - "max": 15, - "min": 2 - }, - "symbol": { - "fixed": "img/icons/marker/circle.svg", - "mode": "fixed" - }, - "textConfig": { - "fontSize": 12, - "offsetX": 0, - "offsetY": 0, - "textAlign": "center", - "textBaseline": "middle" - } - } - }, - "location": { - "gazetteer": "public/gazetteer/usa-states.json", - "lookup": "State", - "mode": "lookup" - }, - "name": "2022 Population", - "tooltip": true, - "type": "markers" - } - ], - "tooltip": { - "mode": "details" - }, - "view": { - "id": "coords", - "lat": 35.70008, - "lon": -93.558296, - "zoom": 3.09 - } - }, - "pluginVersion": "9.2.0-pre", - "targets": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "panelId": 2, - "refId": "A", - "withTransforms": true - } - ], - "title": "Reused data (with transform)", - "type": "geomap" + "type": "dashboard" } - ], - "schemaVersion": 37, - "tags": [ - "gdev", - "transform" - ], - "templating": { - "list": [] + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1394, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "gridPos": { + "h": 3, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 9, + "options": { + "content": "Dashboard queries allow re-using the same results from one panel in another panel context.\n\nThis dashboard shows a single panel that makes a real query and applies transformations. The other panels, all use the same results rather than make their own query requests.", + "mode": "markdown" + }, + "pluginVersion": "9.2.0-pre", + "type": "text" }, - "time": { - "from": "now-6h", - "to": "now" + { + "datasource": { + "type": "grafana-testdata-datasource" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 18, + "w": 7, + "x": 0, + "y": 3 + }, + "id": 2, + "options": { + "footer": { + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "9.2.0-pre", + "targets": [ + { + "csvFileName": "flight_info_by_state.csv", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "A", + "scenarioId": "csv_file" + }, + { + "csvFileName": "population_by_state.csv", + "datasource": { + "type": "grafana-testdata-datasource" + }, + "refId": "B", + "scenarioId": "csv_file" + } + ], + "title": "Raw data -- with outer join", + "transformations": [ + { + "id": "seriesToColumns", + "options": { + "byField": "State", + "mode": "outer" + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "1980 population_by_state.csv": true, + "2000 population_by_state.csv": true, + "DestLocation flight_info_by_state.csv": true, + "Lat flight_info_by_state.csv": true, + "Lng flight_info_by_state.csv": true, + "Price flight_info_by_state.csv": true + }, + "indexByName": {}, + "renameByName": { + "2020 population_by_state.csv": "2020 population", + "Count flight_info_by_state.csv": "Flight count", + "Price flight_info_by_state.csv": "" + } + } + } + ], + "type": "table" }, - "timepicker": {}, - "timezone": "", - "title": "Transforms - Reuse dashboard queries", - "uid": "fYGWTVW4k" - } + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 11, + "x": 7, + "y": 3 + }, + "id": 4, + "options": { + "footer": { + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "9.2.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "title": "Reused data (without transform)", + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 3 + }, + "id": 5, + "options": { + "basemap": { + "config": {}, + "name": "Layer 0", + "type": "default" + }, + "controls": { + "mouseWheelZoom": true, + "showAttribution": true, + "showDebug": false, + "showMeasure": false, + "showScale": false, + "showZoom": true + }, + "layers": [ + { + "config": { + "showLegend": true, + "style": { + "color": { + "fixed": "dark-green" + }, + "opacity": 0.4, + "rotation": { + "fixed": 0, + "max": 360, + "min": -360, + "mode": "mod" + }, + "size": { + "field": "Flight count", + "fixed": 5, + "max": 15, + "min": 2 + }, + "symbol": { + "fixed": "img/icons/marker/circle.svg", + "mode": "fixed" + }, + "textConfig": { + "fontSize": 12, + "offsetX": 0, + "offsetY": 0, + "textAlign": "center", + "textBaseline": "middle" + } + } + }, + "location": { + "gazetteer": "public/gazetteer/usa-states.json", + "lookup": "State", + "mode": "lookup" + }, + "name": "Flight count", + "tooltip": true, + "type": "markers" + } + ], + "tooltip": { + "mode": "details" + }, + "view": { + "id": "coords", + "lat": 35.70008, + "lon": -93.558296, + "zoom": 3.09 + } + }, + "pluginVersion": "9.2.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A", + "withTransforms": true + } + ], + "title": "Reused data (without transform)", + "type": "geomap" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 11, + "x": 7, + "y": 12 + }, + "id": 6, + "options": { + "footer": { + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "9.2.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A", + "withTransforms": true + } + ], + "title": "Reused data (with transform)", + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 18, + "y": 12 + }, + "id": 7, + "options": { + "basemap": { + "config": {}, + "name": "Layer 0", + "type": "default" + }, + "controls": { + "mouseWheelZoom": true, + "showAttribution": true, + "showDebug": false, + "showMeasure": false, + "showScale": false, + "showZoom": true + }, + "layers": [ + { + "config": { + "showLegend": true, + "style": { + "color": { + "fixed": "dark-green" + }, + "opacity": 0.4, + "rotation": { + "fixed": 0, + "max": 360, + "min": -360, + "mode": "mod" + }, + "size": { + "field": "2020 population", + "fixed": 5, + "max": 15, + "min": 2 + }, + "symbol": { + "fixed": "img/icons/marker/circle.svg", + "mode": "fixed" + }, + "textConfig": { + "fontSize": 12, + "offsetX": 0, + "offsetY": 0, + "textAlign": "center", + "textBaseline": "middle" + } + } + }, + "location": { + "gazetteer": "public/gazetteer/usa-states.json", + "lookup": "State", + "mode": "lookup" + }, + "name": "2022 Population", + "tooltip": true, + "type": "markers" + } + ], + "tooltip": { + "mode": "details" + }, + "view": { + "id": "coords", + "lat": 35.70008, + "lon": -93.558296, + "zoom": 3.09 + } + }, + "pluginVersion": "9.2.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A", + "withTransforms": true + } + ], + "title": "Reused data (with transform)", + "type": "geomap" + } + ], + "schemaVersion": 37, + "tags": ["gdev", "transform"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Transforms - Reuse dashboard queries", + "uid": "fYGWTVW4k" +} diff --git a/devenv/dev-dashboards/transforms/rows-to-fields.json b/devenv/dev-dashboards/transforms/rows-to-fields.json index 45b8951c082..d04120f95e9 100644 --- a/devenv/dev-dashboards/transforms/rows-to-fields.json +++ b/devenv/dev-dashboards/transforms/rows-to-fields.json @@ -1,611 +1,598 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "target": { - "limit": 100, - "matchAny": false, - "tags": [], - "type": "dashboard" - }, + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], "type": "dashboard" - } - ] - }, - "editable": true, - "graphTooltip": 0, - "links": [], - "panels": [ - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "left", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 8, - "options": { - "showHeader": true - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "panelId": 2, - "refId": "A" - } - ], - "title": "Raw data", - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "left", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Value" - }, - "properties": [ - { - "id": "custom.width", - "value": 82 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unit" - }, - "properties": [ - { - "id": "custom.width", - "value": 108 - } - ] - } - ] - }, - "gridPos": { - "h": 5, - "w": 12, - "x": 12, - "y": 0 - }, - "id": 7, - "options": { - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "panelId": 3, - "refId": "A" - } - ], - "title": "Raw data", - "type": "table" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 5 - }, - "id": 2, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "csvContent": "Name,Value,Unit,Color\nTemperature,10,degree,green\nPressure,100,bar,blue\nSpeed,30,km/h,red", - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Unit and color from data", - "transformations": [ - { - "id": "rowsToFields", - "options": {} - } - ], - "type": "stat" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 5 - }, - "id": 3, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": true, - "showThresholdMarkers": true, - "text": {} - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "csvContent": "Name,Value,Unit,min,max, threshold1\nTemperature,10,degree,0,50,30\nPressure,100,Pa,0,300,200\nSpeed,30,km/h,0,150,110", - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Min, Max & Thresholds from data", - "transformations": [ - { - "id": "rowsToFields", - "options": {} - } - ], - "type": "gauge" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "left", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 5, - "w": 12, - "x": 0, - "y": 12 - }, - "id": 10, - "options": { - "showHeader": true - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "panelId": 9, - "refId": "A" - } - ], - "title": "Raw data", - "type": "table" - }, - { - "datasource": { - "type": "datasource", - "uid": "-- Dashboard --" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "left", - "displayMode": "auto" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Value" - }, - "properties": [ - { - "id": "custom.width", - "value": 82 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Unit" - }, - "properties": [ - { - "id": "custom.width", - "value": 108 - } - ] - } - ] - }, - "gridPos": { - "h": 5, - "w": 12, - "x": 12, - "y": 12 - }, - "id": 12, - "options": { - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "panelId": 11, - "refId": "A" - } - ], - "title": "Raw data (Custom mapping)", - "type": "table" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "continuous-GrYlRd" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 17 - }, - "id": 9, - "options": { - "displayMode": "gradient", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showUnfilled": true, - "text": {} - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "csvContent": "Name,Value,Unit,Min,Max\nTemperature,20,degree,0,50\nPressure,150,Pa,0,300\nSpeed,100,km/h,0,110", - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Min max from data", - "transformations": [ - { - "id": "rowsToFields", - "options": {} - } - ], - "type": "bargauge" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 17 - }, - "id": 11, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": true, - "showThresholdMarkers": true, - "text": {} - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "csvContent": "Name,Value,Type,Quota, Warning\nTemperature,25,degree,50,30\nPressure,100,Pa,300,200\nSpeed,30,km/h,150,130", - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Custom mapping", - "transformations": [ - { - "id": "rowsToFields", - "options": { - "mappings": [ - { - "configProperty": "unit", - "fieldName": "Type", - "handlerKey": "unit" - }, - { - "configProperty": "max", - "fieldName": "Quota", - "handlerKey": "max" - }, - { - "configProperty": "threshold1", - "fieldName": "Warning", - "handlerKey": "threshold1" - } - ] - } - } - ], - "type": "gauge" - }, - { - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 24 - }, - "id": 13, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "8.1.0-pre", - "targets": [ - { - "csvContent": "Name, City, Country, Value\nSensorA, Stockholm, Sweden, 20\nSensorB, London, England, 50\nSensorC, New York, USA,100", - "refId": "A", - "scenarioId": "csv_content" - } - ], - "title": "Extra string fields to labels", - "transformations": [ - { - "id": "rowsToFields", - "options": {} - } - ], - "type": "stat" + "type": "dashboard" } - ], - "refresh": "", - "schemaVersion": 33, - "tags": [ - "gdev", - "transform" - ], - "templating": { - "list": [] + ] + }, + "editable": true, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "left", + "displayMode": "auto" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 8, + "options": { + "showHeader": true + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "panelId": 2, + "refId": "A" + } + ], + "title": "Raw data", + "type": "table" }, - "time": { - "from": "now-6h", - "to": "now" + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "left", + "displayMode": "auto" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.width", + "value": 82 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Unit" + }, + "properties": [ + { + "id": "custom.width", + "value": 108 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 7, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "panelId": 3, + "refId": "A" + } + ], + "title": "Raw data", + "type": "table" }, - "timepicker": {}, - "timezone": "", - "title": "Transforms - Rows to fields", - "uid": "PMtIInink", - "version": 1 - } + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 5 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "csvContent": "Name,Value,Unit,Color\nTemperature,10,degree,green\nPressure,100,bar,blue\nSpeed,30,km/h,red", + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Unit and color from data", + "transformations": [ + { + "id": "rowsToFields", + "options": {} + } + ], + "type": "stat" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 5 + }, + "id": 3, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "csvContent": "Name,Value,Unit,min,max, threshold1\nTemperature,10,degree,0,50,30\nPressure,100,Pa,0,300,200\nSpeed,30,km/h,0,150,110", + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Min, Max & Thresholds from data", + "transformations": [ + { + "id": "rowsToFields", + "options": {} + } + ], + "type": "gauge" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "left", + "displayMode": "auto" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 12 + }, + "id": 10, + "options": { + "showHeader": true + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "panelId": 9, + "refId": "A" + } + ], + "title": "Raw data", + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "left", + "displayMode": "auto" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "custom.width", + "value": 82 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Unit" + }, + "properties": [ + { + "id": "custom.width", + "value": 108 + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 12 + }, + "id": 12, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "panelId": 11, + "refId": "A" + } + ], + "title": "Raw data (Custom mapping)", + "type": "table" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 9, + "options": { + "displayMode": "gradient", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showUnfilled": true, + "text": {} + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "csvContent": "Name,Value,Unit,Min,Max\nTemperature,20,degree,0,50\nPressure,150,Pa,0,300\nSpeed,100,km/h,0,110", + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Min max from data", + "transformations": [ + { + "id": "rowsToFields", + "options": {} + } + ], + "type": "bargauge" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 11, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showThresholdLabels": true, + "showThresholdMarkers": true, + "text": {} + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "csvContent": "Name,Value,Type,Quota, Warning\nTemperature,25,degree,50,30\nPressure,100,Pa,300,200\nSpeed,30,km/h,150,130", + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Custom mapping", + "transformations": [ + { + "id": "rowsToFields", + "options": { + "mappings": [ + { + "configProperty": "unit", + "fieldName": "Type", + "handlerKey": "unit" + }, + { + "configProperty": "max", + "fieldName": "Quota", + "handlerKey": "max" + }, + { + "configProperty": "threshold1", + "fieldName": "Warning", + "handlerKey": "threshold1" + } + ] + } + } + ], + "type": "gauge" + }, + { + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 13, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "8.1.0-pre", + "targets": [ + { + "csvContent": "Name, City, Country, Value\nSensorA, Stockholm, Sweden, 20\nSensorB, London, England, 50\nSensorC, New York, USA,100", + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Extra string fields to labels", + "transformations": [ + { + "id": "rowsToFields", + "options": {} + } + ], + "type": "stat" + } + ], + "refresh": "", + "schemaVersion": 33, + "tags": ["gdev", "transform"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Transforms - Rows to fields", + "uid": "PMtIInink", + "version": 1 +} diff --git a/devenv/docker/blocks/auth/authentik/README.md b/devenv/docker/blocks/auth/authentik/README.md index 7c02d38bb8d..822fe35595b 100644 --- a/devenv/docker/blocks/auth/authentik/README.md +++ b/devenv/docker/blocks/auth/authentik/README.md @@ -24,7 +24,6 @@ The container will expose port `3389` and `6636` as LDAP and LDAPS. The container will expose port `9000` for API and Admin interface. - ## LDAP Setup The following changes are needed at Grafana's configuration file. @@ -90,13 +89,13 @@ signout_redirect_url = http://localhost:9000/application/o/grafana-oidc/end-sess ## Available users and groups -*authentik admin*: +_authentik admin_: - username: akadmin - email: admin@localhost - password: admin -*grafana logins*: +_grafana logins_: - username: authentik-admin - password: grafana diff --git a/devenv/docker/blocks/auth/jwt_proxy/jwks.json b/devenv/docker/blocks/auth/jwt_proxy/jwks.json index 9f79114ba44..d1a938c0928 100644 --- a/devenv/docker/blocks/auth/jwt_proxy/jwks.json +++ b/devenv/docker/blocks/auth/jwt_proxy/jwks.json @@ -1 +1,17 @@ -{"keys":[{"kid":"On2FQuJ8Y-909uJGWQEDkbzG-GRNmMc43HslEgVv_VQ","kty":"RSA","alg":"RS256","use":"sig","n":"qDmQHfTcOQOzmNJbVvtvuS8p_EgmiscP7vA_PZNyKx9O7utyGuoAmJH8e2w8gLIDDWHl5_x8aAIl_-TTPTSiyX8I68ryIdR28ZSe5u4pRdpXCVvJpOefKNIxQCTH7rs4KuRj0HZ2u1mu1Vz5_CeCCoKwKSmheD3u1xTJ8-VxQmdqfGxhuKtnkof7977HWOWy4GLDFqxyYHgihP_MmSeTmXUhVeZI6IOCqHMpF8eFWVGKM6V8rIKf8QO2K_vDJBM_3C933vMY8mqSQXbI3G54x-0myAaQXr4JkxjvUGKg5YC3ZXw7AjfZv_W_fQOG0GYp2hQ0akR4KNKT3XPNmpMVlQ","e":"AQAB","x5c":["MIICnTCCAYUCBgF+u1ir8jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdncmFmYW5hMB4XDTIyMDIwMjE2NDkxN1oXDTMyMDIwMjE2NTA1N1owEjEQMA4GA1UEAwwHZ3JhZmFuYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKg5kB303DkDs5jSW1b7b7kvKfxIJorHD+7wPz2TcisfTu7rchrqAJiR/HtsPICyAw1h5ef8fGgCJf/k0z00osl/COvK8iHUdvGUnubuKUXaVwlbyaTnnyjSMUAkx+67OCrkY9B2drtZrtVc+fwnggqCsCkpoXg97tcUyfPlcUJnanxsYbirZ5KH+/e+x1jlsuBiwxascmB4IoT/zJknk5l1IVXmSOiDgqhzKRfHhVlRijOlfKyCn/EDtiv7wyQTP9wvd97zGPJqkkF2yNxueMftJsgGkF6+CZMY71BioOWAt2V8OwI32b/1v30DhtBmKdoUNGpEeCjSk91zzZqTFZUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEABlW64QxuREB81VMGsyhj4Q5RykFaVuD5O8YlwUpmVfAVLzb0Drf54Kn4bnpnckKyYV+T+HsN4QXt81UE41xH0Aai2H3vrGH+PJf6aLPCDE+jpMqtN3n6IgImJXJPL8upMfhhWDv4nkM4uynEwWupzmrKi4oJuTETSMktJby4o6//XWnCzCVMoAGFJU4gtjBUzOMLW26zD+yc+BuUtfR3HzItVHSZKQSNSFO0kVS68RgrER8qJw07z3BOJ2bPpPM0PYyEngGMaowz/T6lI32ymGMWYMAnslthS1KAW9xcTBwnrW1nMhe5a0LPxIktys/wJtxIHZLc5sOddGT4xYklLg=="],"x5t":"prs-h1NBqOSJMH-tQWLTqguWets","x5t#S256":"YjK3HobZW8xbNL1IPDgFhCM41UC5c0hG2cxaF6v961Q"}]} \ No newline at end of file +{ + "keys": [ + { + "kid": "On2FQuJ8Y-909uJGWQEDkbzG-GRNmMc43HslEgVv_VQ", + "kty": "RSA", + "alg": "RS256", + "use": "sig", + "n": "qDmQHfTcOQOzmNJbVvtvuS8p_EgmiscP7vA_PZNyKx9O7utyGuoAmJH8e2w8gLIDDWHl5_x8aAIl_-TTPTSiyX8I68ryIdR28ZSe5u4pRdpXCVvJpOefKNIxQCTH7rs4KuRj0HZ2u1mu1Vz5_CeCCoKwKSmheD3u1xTJ8-VxQmdqfGxhuKtnkof7977HWOWy4GLDFqxyYHgihP_MmSeTmXUhVeZI6IOCqHMpF8eFWVGKM6V8rIKf8QO2K_vDJBM_3C933vMY8mqSQXbI3G54x-0myAaQXr4JkxjvUGKg5YC3ZXw7AjfZv_W_fQOG0GYp2hQ0akR4KNKT3XPNmpMVlQ", + "e": "AQAB", + "x5c": [ + "MIICnTCCAYUCBgF+u1ir8jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdncmFmYW5hMB4XDTIyMDIwMjE2NDkxN1oXDTMyMDIwMjE2NTA1N1owEjEQMA4GA1UEAwwHZ3JhZmFuYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKg5kB303DkDs5jSW1b7b7kvKfxIJorHD+7wPz2TcisfTu7rchrqAJiR/HtsPICyAw1h5ef8fGgCJf/k0z00osl/COvK8iHUdvGUnubuKUXaVwlbyaTnnyjSMUAkx+67OCrkY9B2drtZrtVc+fwnggqCsCkpoXg97tcUyfPlcUJnanxsYbirZ5KH+/e+x1jlsuBiwxascmB4IoT/zJknk5l1IVXmSOiDgqhzKRfHhVlRijOlfKyCn/EDtiv7wyQTP9wvd97zGPJqkkF2yNxueMftJsgGkF6+CZMY71BioOWAt2V8OwI32b/1v30DhtBmKdoUNGpEeCjSk91zzZqTFZUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEABlW64QxuREB81VMGsyhj4Q5RykFaVuD5O8YlwUpmVfAVLzb0Drf54Kn4bnpnckKyYV+T+HsN4QXt81UE41xH0Aai2H3vrGH+PJf6aLPCDE+jpMqtN3n6IgImJXJPL8upMfhhWDv4nkM4uynEwWupzmrKi4oJuTETSMktJby4o6//XWnCzCVMoAGFJU4gtjBUzOMLW26zD+yc+BuUtfR3HzItVHSZKQSNSFO0kVS68RgrER8qJw07z3BOJ2bPpPM0PYyEngGMaowz/T6lI32ymGMWYMAnslthS1KAW9xcTBwnrW1nMhe5a0LPxIktys/wJtxIHZLc5sOddGT4xYklLg==" + ], + "x5t": "prs-h1NBqOSJMH-tQWLTqguWets", + "x5t#S256": "YjK3HobZW8xbNL1IPDgFhCM41UC5c0hG2cxaF6v961Q" + } + ] +} diff --git a/devenv/docker/blocks/auth/jwt_proxy/readme.md b/devenv/docker/blocks/auth/jwt_proxy/readme.md index 823287f54e3..f5b05527f96 100644 --- a/devenv/docker/blocks/auth/jwt_proxy/readme.md +++ b/devenv/docker/blocks/auth/jwt_proxy/readme.md @@ -1,4 +1,5 @@ # JWT PROXY BLOCK + ## Devenv setup jwt auth To launch the block, use the oauth source. Ex: @@ -28,13 +29,14 @@ groups_attribute_path = groups[] allow_assign_grafana_admin = true ``` -Add *env.grafana.local* to /etc/hosts (Mac/Linux) or C:\Windows\System32\drivers\etc\hosts (Windows): +Add _env.grafana.local_ to /etc/hosts (Mac/Linux) or C:\Windows\System32\drivers\etc\hosts (Windows): + ```ini 127.0.0.1 env.grafana.local ::1 env.grafana.local ``` -Access Grafana through: +Access Grafana through: ```sh http://env.grafana.local:8088 @@ -72,11 +74,11 @@ docker-compose exec -T oauthkeycloakdb bash -c "pg_dump -U keycloak keycloak" > ## Connecting to keycloak: -- keycloak admin: http://localhost:8087 -- keycloak admin login: admin:admin -- grafana jwt viewer login: jwt-viewer:grafana -- grafana jwt editor login: jwt-editor:grafana -- grafana jwt admin login: jwt-admin:grafana +- keycloak admin: http://localhost:8087 +- keycloak admin login: admin:admin +- grafana jwt viewer login: jwt-viewer:grafana +- grafana jwt editor login: jwt-editor:grafana +- grafana jwt admin login: jwt-admin:grafana # Troubleshooting @@ -86,13 +88,17 @@ The new arm64 architecture does not build for the latest docker image of keycloa Until then you need to build the docker image locally and then run `devenv`. 1. Remove any lingering keycloak image + ```sh $ docker rmi $(docker images | grep 'keycloak') ``` + 1. Build keycloak image locally + ```sh $ ./docker-build-keycloak-m1-image.sh ``` + 1. Start from beginning of this readme ## Docker for Windows Users @@ -102,12 +108,16 @@ $ ./docker-build-keycloak-m1-image.sh Port forwarding needs to be set up between the WSL 2 VM (which runs Grafana, in my case it is Ubuntu) and the host system. (https://docs.microsoft.com/en-us/windows/wsl/networking) Run the following commands from an elevated PowerShell prompt: + 1. Change the default WSL 2 distribution if necessary + ```powershell wsl --list # Find the default wsl -s Ubuntu # Change the default ``` + 2. Open port 3000 between the Windows host and the WSL 2 VM + ```powershell $hostAddr = '0.0.0.0'; $wslHostAddr = wsl hostname -I; diff --git a/devenv/docker/blocks/auth/oauth/jwks.json b/devenv/docker/blocks/auth/oauth/jwks.json index 9f79114ba44..d1a938c0928 100644 --- a/devenv/docker/blocks/auth/oauth/jwks.json +++ b/devenv/docker/blocks/auth/oauth/jwks.json @@ -1 +1,17 @@ -{"keys":[{"kid":"On2FQuJ8Y-909uJGWQEDkbzG-GRNmMc43HslEgVv_VQ","kty":"RSA","alg":"RS256","use":"sig","n":"qDmQHfTcOQOzmNJbVvtvuS8p_EgmiscP7vA_PZNyKx9O7utyGuoAmJH8e2w8gLIDDWHl5_x8aAIl_-TTPTSiyX8I68ryIdR28ZSe5u4pRdpXCVvJpOefKNIxQCTH7rs4KuRj0HZ2u1mu1Vz5_CeCCoKwKSmheD3u1xTJ8-VxQmdqfGxhuKtnkof7977HWOWy4GLDFqxyYHgihP_MmSeTmXUhVeZI6IOCqHMpF8eFWVGKM6V8rIKf8QO2K_vDJBM_3C933vMY8mqSQXbI3G54x-0myAaQXr4JkxjvUGKg5YC3ZXw7AjfZv_W_fQOG0GYp2hQ0akR4KNKT3XPNmpMVlQ","e":"AQAB","x5c":["MIICnTCCAYUCBgF+u1ir8jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdncmFmYW5hMB4XDTIyMDIwMjE2NDkxN1oXDTMyMDIwMjE2NTA1N1owEjEQMA4GA1UEAwwHZ3JhZmFuYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKg5kB303DkDs5jSW1b7b7kvKfxIJorHD+7wPz2TcisfTu7rchrqAJiR/HtsPICyAw1h5ef8fGgCJf/k0z00osl/COvK8iHUdvGUnubuKUXaVwlbyaTnnyjSMUAkx+67OCrkY9B2drtZrtVc+fwnggqCsCkpoXg97tcUyfPlcUJnanxsYbirZ5KH+/e+x1jlsuBiwxascmB4IoT/zJknk5l1IVXmSOiDgqhzKRfHhVlRijOlfKyCn/EDtiv7wyQTP9wvd97zGPJqkkF2yNxueMftJsgGkF6+CZMY71BioOWAt2V8OwI32b/1v30DhtBmKdoUNGpEeCjSk91zzZqTFZUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEABlW64QxuREB81VMGsyhj4Q5RykFaVuD5O8YlwUpmVfAVLzb0Drf54Kn4bnpnckKyYV+T+HsN4QXt81UE41xH0Aai2H3vrGH+PJf6aLPCDE+jpMqtN3n6IgImJXJPL8upMfhhWDv4nkM4uynEwWupzmrKi4oJuTETSMktJby4o6//XWnCzCVMoAGFJU4gtjBUzOMLW26zD+yc+BuUtfR3HzItVHSZKQSNSFO0kVS68RgrER8qJw07z3BOJ2bPpPM0PYyEngGMaowz/T6lI32ymGMWYMAnslthS1KAW9xcTBwnrW1nMhe5a0LPxIktys/wJtxIHZLc5sOddGT4xYklLg=="],"x5t":"prs-h1NBqOSJMH-tQWLTqguWets","x5t#S256":"YjK3HobZW8xbNL1IPDgFhCM41UC5c0hG2cxaF6v961Q"}]} \ No newline at end of file +{ + "keys": [ + { + "kid": "On2FQuJ8Y-909uJGWQEDkbzG-GRNmMc43HslEgVv_VQ", + "kty": "RSA", + "alg": "RS256", + "use": "sig", + "n": "qDmQHfTcOQOzmNJbVvtvuS8p_EgmiscP7vA_PZNyKx9O7utyGuoAmJH8e2w8gLIDDWHl5_x8aAIl_-TTPTSiyX8I68ryIdR28ZSe5u4pRdpXCVvJpOefKNIxQCTH7rs4KuRj0HZ2u1mu1Vz5_CeCCoKwKSmheD3u1xTJ8-VxQmdqfGxhuKtnkof7977HWOWy4GLDFqxyYHgihP_MmSeTmXUhVeZI6IOCqHMpF8eFWVGKM6V8rIKf8QO2K_vDJBM_3C933vMY8mqSQXbI3G54x-0myAaQXr4JkxjvUGKg5YC3ZXw7AjfZv_W_fQOG0GYp2hQ0akR4KNKT3XPNmpMVlQ", + "e": "AQAB", + "x5c": [ + "MIICnTCCAYUCBgF+u1ir8jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdncmFmYW5hMB4XDTIyMDIwMjE2NDkxN1oXDTMyMDIwMjE2NTA1N1owEjEQMA4GA1UEAwwHZ3JhZmFuYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKg5kB303DkDs5jSW1b7b7kvKfxIJorHD+7wPz2TcisfTu7rchrqAJiR/HtsPICyAw1h5ef8fGgCJf/k0z00osl/COvK8iHUdvGUnubuKUXaVwlbyaTnnyjSMUAkx+67OCrkY9B2drtZrtVc+fwnggqCsCkpoXg97tcUyfPlcUJnanxsYbirZ5KH+/e+x1jlsuBiwxascmB4IoT/zJknk5l1IVXmSOiDgqhzKRfHhVlRijOlfKyCn/EDtiv7wyQTP9wvd97zGPJqkkF2yNxueMftJsgGkF6+CZMY71BioOWAt2V8OwI32b/1v30DhtBmKdoUNGpEeCjSk91zzZqTFZUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEABlW64QxuREB81VMGsyhj4Q5RykFaVuD5O8YlwUpmVfAVLzb0Drf54Kn4bnpnckKyYV+T+HsN4QXt81UE41xH0Aai2H3vrGH+PJf6aLPCDE+jpMqtN3n6IgImJXJPL8upMfhhWDv4nkM4uynEwWupzmrKi4oJuTETSMktJby4o6//XWnCzCVMoAGFJU4gtjBUzOMLW26zD+yc+BuUtfR3HzItVHSZKQSNSFO0kVS68RgrER8qJw07z3BOJ2bPpPM0PYyEngGMaowz/T6lI32ymGMWYMAnslthS1KAW9xcTBwnrW1nMhe5a0LPxIktys/wJtxIHZLc5sOddGT4xYklLg==" + ], + "x5t": "prs-h1NBqOSJMH-tQWLTqguWets", + "x5t#S256": "YjK3HobZW8xbNL1IPDgFhCM41UC5c0hG2cxaF6v961Q" + } + ] +} diff --git a/devenv/docker/blocks/auth/oauth/readme.md b/devenv/docker/blocks/auth/oauth/readme.md index cd59c21a846..51fdd7526cc 100644 --- a/devenv/docker/blocks/auth/oauth/readme.md +++ b/devenv/docker/blocks/auth/oauth/readme.md @@ -3,6 +3,7 @@ ## Devenv setup oauth To launch the block, use the oauth source. Ex: + ```bash make devenv sources="auth/oauth" ``` @@ -67,7 +68,6 @@ curl --request POST \ --data password=grafana ``` - Grafana call example: ```sh @@ -112,9 +112,9 @@ docker-compose exec -T oauthkeycloakdb bash -c "pg_dump -U keycloak keycloak" > ## Connecting to keycloak: -- keycloak admin: http://localhost:8087 -- keycloak admin login: admin:admin -- grafana oauth viewer login: oauth-viewer:grafana -- grafana oauth editor login: oauth-editor:grafana -- grafana oauth admin login: oauth-admin:grafana -- grafana oauth server admin login: oauth-grafanaadmin:grafana +- keycloak admin: http://localhost:8087 +- keycloak admin login: admin:admin +- grafana oauth viewer login: oauth-viewer:grafana +- grafana oauth editor login: oauth-editor:grafana +- grafana oauth admin login: oauth-admin:grafana +- grafana oauth server admin login: oauth-grafanaadmin:grafana diff --git a/devenv/docker/blocks/auth/openldap-multiple/README.md b/devenv/docker/blocks/auth/openldap-multiple/README.md index b31939c9bbd..ea3bbb8aea7 100644 --- a/devenv/docker/blocks/auth/openldap-multiple/README.md +++ b/devenv/docker/blocks/auth/openldap-multiple/README.md @@ -30,9 +30,10 @@ enabled = true config_file = ./devenv/docker/blocks/auth/openldap-multiple/ldap_dev.toml ``` -## Available users and groups +## Available users and groups ### Srv1 (dc=srv1-grafana,dc=org) + - admins - ldap-admin-srv1 - editors diff --git a/devenv/docker/blocks/auth/openldap/README.md b/devenv/docker/blocks/auth/openldap/README.md index ee86f0daab4..a06efee58f4 100644 --- a/devenv/docker/blocks/auth/openldap/README.md +++ b/devenv/docker/blocks/auth/openldap/README.md @@ -1,6 +1,6 @@ # OpenLDAP for MacOS Docker Block -This Docker block is an updated version from [OpenLDAP](../openldap/) block. This Docker block uses `osixia/openldap` image. The original Docker block was based of `debian:jessie` which is not available for Apple's ARM chip. +This Docker block is an updated version from [OpenLDAP](../openldap/) block. This Docker block uses `osixia/openldap` image. The original Docker block was based of `debian:jessie` which is not available for Apple's ARM chip. ## Deployment @@ -58,6 +58,7 @@ The default configuration between Grafana and the OpenLDAP container is configur ## Configure LDAP with TLS After the `openldap` container has been deployed, you have to copy the CA from the docker container: + ```bash # get the container ID docker ps @@ -85,7 +86,8 @@ port = 636 use_ssl = true start_tls = false ``` -- To connect with STARTTLS use this config: + +- To connect with STARTTLS use this config: ```ini port = 389 diff --git a/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/README.md b/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/README.md index e8af6516516..4d9bdc2878c 100644 --- a/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/README.md +++ b/devenv/docker/blocks/auth/prometheus_oauth2_proxy_azure/README.md @@ -5,7 +5,7 @@ 1. Make a copy of `oauth2-proxy.example.cfg` and rename it to `oauth2-proxy.cfg` 1. Fill in the required information (`azure client id`, `azure client secret`, `azure tenant id`) 1. Start the containers by executing `make devenv sources="prometheus,auth/prometheus_oauth2_proxy_azure"` -> If you would like to test the login flow from the browser then you need to setup TLS or start a tunnel. I usually use a tunnel (`cloudflared tunnel --url http://localhost:4180`). Do not forget to set the Redirect URIs on Azure's App Registration page + > If you would like to test the login flow from the browser then you need to setup TLS or start a tunnel. I usually use a tunnel (`cloudflared tunnel --url http://localhost:4180`). Do not forget to set the Redirect URIs on Azure's App Registration page ## How to add a new Prometheus datasource with Azure Authentication enabled @@ -15,11 +15,12 @@ 1. Set the URL 1. Enable Azure Authentication 1. Fill in the required fields of the `Azure Authentication` section - 1. Click `Save & test` + 1. Click `Save & test` 1. You should get a "Data source is working" message If you check the logs of OAuth2-proxy, you should see similar lines to this: + ``` 2023-04-19 11:29:40 172.31.0.1:55602 - d96b832a-170a-41eb-a974-6558c5ce4454 - - [2023/04/19 09:29:40] some-random-tunnel-address.trycloudflare.com GET / "/api/v1/status/buildinfo" HTTP/1.1 "Grafana/10.0.0-pre" 200 187 0.016 2023-04-19 11:29:41 172.31.0.1:55602 - db27c56a-ccd6-4cdb-a040-318113781abf - 65ac87f4-931f-4e46-9761-f8bf1ad36b48 [2023/04/19 09:29:41] some-random-tunnel-address.trycloudflare.com POST / "/api/v1/query" HTTP/1.1 "Grafana/10.0.0-pre" 200 103 0.003 -``` \ No newline at end of file +``` diff --git a/devenv/docker/blocks/caddy_tls/README.md b/devenv/docker/blocks/caddy_tls/README.md index 464f076ffcc..00a0a86d68f 100644 --- a/devenv/docker/blocks/caddy_tls/README.md +++ b/devenv/docker/blocks/caddy_tls/README.md @@ -5,7 +5,7 @@ Starts a [Caddy server](https://caddyserver.com/) with TLS configured. ## Setup - Caddy is setup to run on port 2081, so when configuring the webhook receiver in Grafana Alerting you should use the -following the following URL: `https://localhost:2081` + following the following URL: `https://localhost:2081` - Also, Caddy is configured to use a self-signed certificate and to check the client certificate (`require_and_verify` mode) - Caddy is setup to log requests and has debug mode enabled to make it easier to investigate possible issues @@ -15,19 +15,19 @@ If you want to configure a webhook contact point in Grafana Alerting with TLS, y You can find them in `/etc/caddy` directory in the container: -``` shell +```shell docker exec devenv-caddy_tls-1 ls /etc/caddy/ ``` ### CA Certificate -``` shell +```shell docker exec devenv-caddy_tls-1 cat /etc/caddy/ca.pem ``` ### Client certificates -``` shell +```shell docker exec devenv-caddy_tls-1 cat /etc/caddy/client.pem docker exec devenv-caddy_tls-1 cat /etc/caddy/client.key ``` diff --git a/devenv/docker/blocks/collectd/README.md b/devenv/docker/blocks/collectd/README.md index ebd744284d1..d950a4677b2 100644 --- a/devenv/docker/blocks/collectd/README.md +++ b/devenv/docker/blocks/collectd/README.md @@ -1,5 +1,4 @@ -collectd-write-graphite -======================= +# collectd-write-graphite Basic collectd-based server monitoring. Sends stats to Graphite. @@ -14,8 +13,7 @@ Collectd metrics: - Network interface - Swap -Environment variables ---------------------- +## Environment variables - `HOST_NAME` - Will be sent to Graphite diff --git a/devenv/docker/blocks/loki-promtail/README.md b/devenv/docker/blocks/loki-promtail/README.md index ef92ddbd193..9bd79ad7f0d 100644 --- a/devenv/docker/blocks/loki-promtail/README.md +++ b/devenv/docker/blocks/loki-promtail/README.md @@ -1,10 +1,11 @@ NB: This will not work properly on MacOS. The loglines of grafana.log are ingested at the start of this devenv and you won't get any more logs are the Docker service is started. By default this block is setup to scrape logs from Grafana. If you need to log some service from the docker-compse you can add: + ``` # For this to work you need to install the logging driver see https://github.com/grafana/loki/tree/master/cmd/docker-driver#plugin-installation logging: driver: loki options: loki-url: "http://loki:3100/loki/api/v1/push" -``` \ No newline at end of file +``` diff --git a/devenv/docker/blocks/maildev/README.md b/devenv/docker/blocks/maildev/README.md index 6ad77fc4935..77019d6cd85 100644 --- a/devenv/docker/blocks/maildev/README.md +++ b/devenv/docker/blocks/maildev/README.md @@ -1 +1 @@ -This block starts a test SMTP server on port 1025 and UI on port 12080 \ No newline at end of file +This block starts a test SMTP server on port 1025 and UI on port 12080 diff --git a/devenv/docker/blocks/mqtt/README.md b/devenv/docker/blocks/mqtt/README.md index 6724fa17666..f7dd0ff95a2 100644 --- a/devenv/docker/blocks/mqtt/README.md +++ b/devenv/docker/blocks/mqtt/README.md @@ -5,7 +5,7 @@ Starts a [NanoMQ MQTT broker](https://nanomq.io/docs/en/latest/). ## Authentication The broker is configured to use a simple username/password authentication. -See [./nanomq_pwd.conf](./nanomq_pwd.conf) for the default credentials. +See [./nanomq_pwd.conf](./nanomq_pwd.conf) for the default credentials. ## TLS Certificates @@ -13,19 +13,19 @@ If you want to configure an MQTT contact point in Grafana Alerting with TLS, you You can find them in `/etc/certs` directory in the container: -``` shell +```shell docker exec devenv-mqtt-1 ls /etc/certs/ ``` ### CA Certificate -``` shell +```shell docker exec devenv-mqtt-1 cat /etc/certs/ca.pem ``` ### Client certificates -``` shell +```shell docker exec devenv-mqtt-1 cat /etc/certs/client.pem docker exec devenv-mqtt-1 cat /etc/certs/client.key ``` diff --git a/devenv/docker/blocks/mssql_arm64/README.md b/devenv/docker/blocks/mssql_arm64/README.md index 83c63a63fe8..c45f007caf5 100644 --- a/devenv/docker/blocks/mssql_arm64/README.md +++ b/devenv/docker/blocks/mssql_arm64/README.md @@ -6,7 +6,8 @@ The `setup` files cannot be used to create a custom `grafana` db/user since `sql (https://docs.microsoft.com/en-us/azure/azure-sql-edge/connect) You can connect the Grafana MSSQL datasource to this instance by using the following credentials: - * Host: `localhost` - * Database: `master` - * User: `sa` - * Password: `Password!` or whatever is set in `docker-compose.yaml` for env var `MSSQL_SA_PASSWORD` \ No newline at end of file + +- Host: `localhost` +- Database: `master` +- User: `sa` +- Password: `Password!` or whatever is set in `docker-compose.yaml` for env var `MSSQL_SA_PASSWORD` diff --git a/devenv/docker/blocks/self-instrumentation/readme.md b/devenv/docker/blocks/self-instrumentation/readme.md index 76ef72d1ed4..07fc5856cf6 100644 --- a/devenv/docker/blocks/self-instrumentation/readme.md +++ b/devenv/docker/blocks/self-instrumentation/readme.md @@ -28,4 +28,4 @@ To collect profiles with pyroscope, you need to run Grafana with the following e export GF_DIAGNOSTICS_PROFILING_ENABLED=true export GF_DIAGNOSTICS_PROFILING_ADDR=0.0.0.0 make run -`````` +``` diff --git a/devenv/docker/blocks/sensugo/notes.md b/devenv/docker/blocks/sensugo/notes.md index f6f0b7e74b8..cfedc43da4a 100644 --- a/devenv/docker/blocks/sensugo/notes.md +++ b/devenv/docker/blocks/sensugo/notes.md @@ -24,7 +24,6 @@ The output should look similar to this: Created: /api/core/v2/apikeys/0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d ``` - ## Configuring the notification channel ### Backend URL @@ -36,4 +35,3 @@ The Backend URL is the API port (8080) forwarded to the container, it should be The `0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d` in the output above is the API Key to use in configuring the Sensu Go notification channel. - diff --git a/devenv/docker/blocks/tempo/README.md b/devenv/docker/blocks/tempo/README.md index 39d586dd5c8..1829b514bf7 100644 --- a/devenv/docker/blocks/tempo/README.md +++ b/devenv/docker/blocks/tempo/README.md @@ -1,4 +1,5 @@ This devenv docker-compose.yaml will allow you to; + - search traces - view traces - upload/download trace JSON files @@ -6,6 +7,7 @@ This devenv docker-compose.yaml will allow you to; - search traces via Loki To send traces from grafana use this configuration; + ``` [tracing.opentelemetry.otlp] # otlp destination (ex localhost:4317) diff --git a/devenv/docker/ha-test-unified-alerting/README.md b/devenv/docker/ha-test-unified-alerting/README.md index 48400d91abc..631498866c7 100644 --- a/devenv/docker/ha-test-unified-alerting/README.md +++ b/devenv/docker/ha-test-unified-alerting/README.md @@ -52,15 +52,20 @@ $ docker-compose up -d ``` Browse + - http://grafana.loc/ - http://prometheus.loc/ - ## Test alerting ### Create contact points + TBD + ### Create alerts + TBD + ### Create silences + TBD diff --git a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json index 0357c3d10f1..822052ac4ab 100644 --- a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json +++ b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/alerts/overview.json @@ -140,21 +140,10 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Overview", "uid": "xHy7-hAik", "version": 6 -} \ No newline at end of file +} diff --git a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json index 2bf789366cc..204c8b392d6 100644 --- a/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json +++ b/devenv/docker/ha-test-unified-alerting/grafana/provisioning/dashboards/mysql/overview.json @@ -10,9 +10,7 @@ "limit": 100, "name": "PMM Annotations", "showIn": 0, - "tags": [ - "pmm_annotation" - ], + "tags": ["pmm_annotation"], "type": "tags" }, { @@ -24,9 +22,7 @@ "limit": 100, "name": "Annotations & Alerts", "showIn": 0, - "tags": [ - - ], + "tags": [], "type": "dashboard" } ] @@ -41,9 +37,7 @@ "icon": "dashboard", "includeVars": true, "keepTime": true, - "tags": [ - "QAN" - ], + "tags": ["QAN"], "targetBlank": false, "title": "Query Analytics", "type": "link", @@ -53,9 +47,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "OS" - ], + "tags": ["OS"], "targetBlank": false, "title": "OS", "type": "dashboards" @@ -64,9 +56,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "MySQL" - ], + "tags": ["MySQL"], "targetBlank": false, "title": "MySQL", "type": "dashboards" @@ -75,9 +65,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "MongoDB" - ], + "tags": ["MongoDB"], "targetBlank": false, "title": "MongoDB", "type": "dashboards" @@ -86,9 +74,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "PostgreSQL" - ], + "tags": ["PostgreSQL"], "targetBlank": false, "title": "PostgreSQL", "type": "dashboards" @@ -97,9 +83,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "HA" - ], + "tags": ["HA"], "targetBlank": false, "title": "HA", "type": "dashboards" @@ -108,9 +92,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "Cloud" - ], + "tags": ["Cloud"], "targetBlank": false, "title": "Cloud", "type": "dashboards" @@ -119,9 +101,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "Insight" - ], + "tags": ["Insight"], "targetBlank": false, "title": "Insight", "type": "dashboards" @@ -130,9 +110,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "PMM" - ], + "tags": ["PMM"], "targetBlank": false, "title": "PMM", "type": "dashboards" @@ -148,9 +126,7 @@ "y": 0 }, "id": 382, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "", "type": "row" @@ -159,11 +135,7 @@ "cacheTimeout": null, "colorBackground": false, "colorValue": true, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], "datasource": "Prometheus", "decimals": 1, "description": "**MySQL Uptime**\n\nThe amount of time since the last restart of the MySQL server process.", @@ -186,9 +158,7 @@ "height": "125px", "id": 12, "interval": "$interval", - "links": [ - - ], + "links": [], "mappingType": 1, "mappingTypes": [ { @@ -224,12 +194,8 @@ "targets": [ { "calculatedInterval": "10m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_uptime{instance=\"$host\"}", "format": "time_series", "interval": "5m", @@ -245,20 +211,14 @@ "transparent": false, "type": "singlestat", "valueFontSize": "80%", - "valueMaps": [ - - ], + "valueMaps": [], "valueName": "current" }, { "cacheTimeout": null, "colorBackground": false, "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], "datasource": "Prometheus", "decimals": 2, "description": "**Current QPS**\n\nBased on the queries reported by MySQL's ``SHOW STATUS`` command, it is the number of statements executed by the server within the last second. This variable includes statements executed within stored programs, unlike the Questions variable. It does not count \n``COM_PING`` or ``COM_STATISTICS`` commands.", @@ -324,12 +284,8 @@ "targets": [ { "calculatedInterval": "10m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_queries{instance=\"$host\"}[$interval]) or irate(mysql_global_status_queries{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -345,20 +301,14 @@ "transparent": false, "type": "singlestat", "valueFontSize": "80%", - "valueMaps": [ - - ], + "valueMaps": [], "valueName": "current" }, { "cacheTimeout": null, "colorBackground": false, "colorValue": false, - "colors": [ - "rgba(50, 172, 45, 0.97)", - "rgba(237, 129, 40, 0.89)", - "rgba(245, 54, 54, 0.9)" - ], + "colors": ["rgba(50, 172, 45, 0.97)", "rgba(237, 129, 40, 0.89)", "rgba(245, 54, 54, 0.9)"], "datasource": "Prometheus", "decimals": 0, "description": "**InnoDB Buffer Pool Size**\n\nInnoDB maintains a storage area called the buffer pool for caching data and indexes in memory. Knowing how the InnoDB buffer pool works, and taking advantage of it to keep frequently accessed data in memory, is one of the most important aspects of MySQL tuning. The goal is to keep the working set in memory. In most cases, this should be between 60%-90% of available memory on a dedicated database host, but depends on many factors.", @@ -424,12 +374,8 @@ "targets": [ { "calculatedInterval": "10m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_innodb_buffer_pool_size{instance=\"$host\"}", "format": "time_series", "interval": "5m", @@ -445,20 +391,14 @@ "transparent": false, "type": "singlestat", "valueFontSize": "80%", - "valueMaps": [ - - ], + "valueMaps": [], "valueName": "current" }, { "cacheTimeout": null, "colorBackground": false, "colorValue": true, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], "datasource": "Prometheus", "decimals": 0, "description": "**InnoDB Buffer Pool Size % of Total RAM**\n\nInnoDB maintains a storage area called the buffer pool for caching data and indexes in memory. Knowing how the InnoDB buffer pool works, and taking advantage of it to keep frequently accessed data in memory, is one of the most important aspects of MySQL tuning. The goal is to keep the working set in memory. In most cases, this should be between 60%-90% of available memory on a dedicated database host, but depends on many factors.", @@ -527,12 +467,8 @@ "targets": [ { "calculatedInterval": "10m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "(mysql_global_variables_innodb_buffer_pool_size{instance=\"$host\"} * 100) / on (instance) node_memory_MemTotal{instance=\"$host\"}", "format": "time_series", "interval": "5m", @@ -548,9 +484,7 @@ "transparent": false, "type": "singlestat", "valueFontSize": "80%", - "valueMaps": [ - - ], + "valueMaps": [], "valueName": "current" }, { @@ -562,17 +496,13 @@ "y": 3 }, "id": 383, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Connections", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -582,9 +512,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -632,12 +560,8 @@ "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "max(max_over_time(mysql_global_status_threads_connected{instance=\"$host\"}[$interval]) or mysql_global_status_threads_connected{instance=\"$host\"} )", "format": "time_series", "interval": "$interval", @@ -649,12 +573,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_max_used_connections{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -667,12 +587,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_max_connections{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -684,9 +600,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Connections", @@ -702,9 +616,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -730,9 +642,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -742,9 +652,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -767,9 +675,7 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, @@ -798,12 +704,8 @@ "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "max_over_time(mysql_global_status_threads_connected{instance=\"$host\"}[$interval]) or\nmax_over_time(mysql_global_status_threads_connected{instance=\"$host\"}[5m])", "format": "time_series", "hide": false, @@ -816,12 +718,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "max_over_time(mysql_global_status_threads_running{instance=\"$host\"}[$interval]) or\nmax_over_time(mysql_global_status_threads_running{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -841,9 +739,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Client Thread Activity", @@ -859,9 +755,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - "total" - ] + "values": ["total"] }, "yaxes": [ { @@ -895,17 +789,13 @@ "y": 11 }, "id": 384, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Table Locks", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -915,9 +805,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -953,21 +841,15 @@ "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_questions{instance=\"$host\"}[$interval]) or irate(mysql_global_status_questions{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -978,9 +860,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Questions", @@ -996,9 +876,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1022,9 +900,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1034,9 +910,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1083,12 +957,8 @@ "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_thread_cache_size{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -1100,12 +970,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_threads_cached{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -1117,12 +983,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_threads_created{instance=\"$host\"}[$interval]) or irate(mysql_global_status_threads_created{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1133,9 +995,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Thread Cache", @@ -1152,9 +1012,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1186,17 +1044,13 @@ "y": 19 }, "id": 385, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Temporary Objects", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1205,9 +1059,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1230,29 +1082,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_created_tmp_tables{instance=\"$host\"}[$interval]) or irate(mysql_global_status_created_tmp_tables{instance=\"$host\"}[5m])", "interval": "$interval", "intervalFactor": 1, @@ -1263,12 +1107,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_created_tmp_disk_tables{instance=\"$host\"}[$interval]) or irate(mysql_global_status_created_tmp_disk_tables{instance=\"$host\"}[5m])", "interval": "$interval", "intervalFactor": 1, @@ -1279,12 +1119,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_created_tmp_files{instance=\"$host\"}[$interval]) or irate(mysql_global_status_created_tmp_files{instance=\"$host\"}[5m])", "interval": "$interval", "intervalFactor": 1, @@ -1294,9 +1130,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Temporary Objects", @@ -1312,9 +1146,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1338,9 +1170,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1350,9 +1180,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1377,29 +1205,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_select_full_join{instance=\"$host\"}[$interval]) or irate(mysql_global_status_select_full_join{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1411,12 +1231,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_select_full_range_join{instance=\"$host\"}[$interval]) or irate(mysql_global_status_select_full_range_join{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1428,12 +1244,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_select_range{instance=\"$host\"}[$interval]) or irate(mysql_global_status_select_range{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1445,12 +1257,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_select_range_check{instance=\"$host\"}[$interval]) or irate(mysql_global_status_select_range_check{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1462,12 +1270,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_select_scan{instance=\"$host\"}[$interval]) or irate(mysql_global_status_select_scan{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1478,9 +1282,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Select Types", @@ -1496,9 +1298,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1530,17 +1330,13 @@ "y": 27 }, "id": 386, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Sorts", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1550,9 +1346,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1576,29 +1370,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_sort_rows{instance=\"$host\"}[$interval]) or irate(mysql_global_status_sort_rows{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1610,12 +1396,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_sort_range{instance=\"$host\"}[$interval]) or irate(mysql_global_status_sort_range{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1627,12 +1409,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_sort_merge_passes{instance=\"$host\"}[$interval]) or irate(mysql_global_status_sort_merge_passes{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1644,12 +1422,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_sort_scan{instance=\"$host\"}[$interval]) or irate(mysql_global_status_sort_scan{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1660,9 +1434,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Sorts", @@ -1678,9 +1450,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1704,9 +1474,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1716,9 +1484,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1740,29 +1506,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_slow_queries{instance=\"$host\"}[$interval]) or irate(mysql_global_status_slow_queries{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1773,9 +1531,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Slow Queries", @@ -1791,9 +1547,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1827,17 +1581,13 @@ "y": 35 }, "id": 387, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Aborted", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1847,9 +1597,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1871,29 +1619,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_aborted_connects{instance=\"$host\"}[$interval]) or irate(mysql_global_status_aborted_connects{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1905,12 +1645,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_aborted_clients{instance=\"$host\"}[$interval]) or irate(mysql_global_status_aborted_clients{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1922,9 +1658,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Aborted Connections", @@ -1940,9 +1674,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1968,9 +1700,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1980,9 +1710,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -2005,29 +1733,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_table_locks_immediate{instance=\"$host\"}[$interval]) or irate(mysql_global_status_table_locks_immediate{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -2039,12 +1759,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_table_locks_waited{instance=\"$host\"}[$interval]) or irate(mysql_global_status_table_locks_waited{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -2055,9 +1771,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Table Locks", @@ -2073,9 +1787,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2107,17 +1819,13 @@ "y": 43 }, "id": 388, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Network", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -2127,9 +1835,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -2152,29 +1858,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": true, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_bytes_received{instance=\"$host\"}[$interval]) or irate(mysql_global_status_bytes_received{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -2186,12 +1884,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_bytes_sent{instance=\"$host\"}[$interval]) or irate(mysql_global_status_bytes_sent{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -2202,9 +1896,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Network Traffic", @@ -2220,9 +1912,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2246,9 +1936,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": true, "dashLength": 10, "dashes": false, @@ -2258,9 +1946,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -2284,29 +1970,21 @@ }, "lines": false, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": true, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "increase(mysql_global_status_bytes_received{instance=\"$host\"}[1h])", "format": "time_series", "interval": "1h", @@ -2318,12 +1996,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "increase(mysql_global_status_bytes_sent{instance=\"$host\"}[1h])", "format": "time_series", "interval": "1h", @@ -2334,9 +2008,7 @@ "step": 3600 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": "24h", "timeShift": null, "title": "MySQL Network Usage Hourly", @@ -2352,9 +2024,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2386,17 +2056,13 @@ "y": 51 }, "id": 389, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Memory", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -2406,9 +2072,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -2538,9 +2202,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Internal Memory Overview", @@ -2556,9 +2218,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2592,17 +2252,13 @@ "y": 59 }, "id": 390, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Command, Handlers, Processes", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -2612,9 +2268,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -2651,21 +2305,15 @@ "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "topk(5, rate(mysql_global_status_commands_total{instance=\"$host\"}[$interval])>0) or topk(5, irate(mysql_global_status_commands_total{instance=\"$host\"}[5m])>0)", "format": "time_series", "hide": false, @@ -2677,9 +2325,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Top Command Counters", @@ -2695,9 +2341,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2721,9 +2365,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": true, "dashLength": 10, "dashes": false, @@ -2733,9 +2375,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -2771,21 +2411,15 @@ "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": true, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "topk(5, increase(mysql_global_status_commands_total{instance=\"$host\"}[1h])>0)", "format": "time_series", "interval": "1h", @@ -2796,9 +2430,7 @@ "step": 3600 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": "24h", "timeShift": null, "title": "Top Command Counters Hourly", @@ -2814,9 +2446,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2840,9 +2470,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -2852,9 +2480,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -2878,29 +2504,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_handlers_total{instance=\"$host\", handler!~\"commit|rollback|savepoint.*|prepare\"}[$interval]) or irate(mysql_global_status_handlers_total{instance=\"$host\", handler!~\"commit|rollback|savepoint.*|prepare\"}[5m])", "format": "time_series", "interval": "$interval", @@ -2911,9 +2529,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Handlers", @@ -2929,9 +2545,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2955,9 +2569,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -2966,9 +2578,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -2992,29 +2602,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_handlers_total{instance=\"$host\", handler=~\"commit|rollback|savepoint.*|prepare\"}[$interval]) or irate(mysql_global_status_handlers_total{instance=\"$host\", handler=~\"commit|rollback|savepoint.*|prepare\"}[5m])", "interval": "$interval", "intervalFactor": 1, @@ -3024,9 +2626,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Transaction Handlers", @@ -3042,9 +2642,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3068,9 +2666,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3079,9 +2675,7 @@ "editable": true, "error": false, "fill": 0, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -3105,29 +2699,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_info_schema_threads{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -3138,9 +2724,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Process States", @@ -3156,9 +2740,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3182,9 +2764,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": true, "dashLength": 10, "dashes": false, @@ -3193,9 +2773,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -3219,29 +2797,21 @@ }, "lines": false, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": true, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "topk(5, avg_over_time(mysql_info_schema_threads{instance=\"$host\"}[1h]))", "interval": "1h", "intervalFactor": 1, @@ -3251,9 +2821,7 @@ "step": 3600 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": "24h", "timeShift": null, "title": "Top Process States Hourly", @@ -3269,9 +2837,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3303,17 +2869,13 @@ "y": 102 }, "id": 391, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Query Cache", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3323,9 +2885,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -3348,29 +2908,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_qcache_free_memory{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -3382,12 +2934,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_query_cache_size{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -3398,9 +2946,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Query Cache Memory", @@ -3416,9 +2962,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3442,9 +2986,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3454,9 +2996,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -3480,29 +3020,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_qcache_hits{instance=\"$host\"}[$interval]) or irate(mysql_global_status_qcache_hits{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -3514,12 +3046,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_qcache_inserts{instance=\"$host\"}[$interval]) or irate(mysql_global_status_qcache_inserts{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -3531,12 +3059,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_qcache_not_cached{instance=\"$host\"}[$interval]) or irate(mysql_global_status_qcache_not_cached{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -3548,12 +3072,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_qcache_lowmem_prunes{instance=\"$host\"}[$interval]) or irate(mysql_global_status_qcache_lowmem_prunes{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -3565,12 +3085,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_qcache_queries_in_cache{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -3581,9 +3097,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Query Cache Activity", @@ -3599,9 +3113,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3633,17 +3145,13 @@ "y": 110 }, "id": 392, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Files and Tables", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3652,9 +3160,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -3677,29 +3183,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_opened_files{instance=\"$host\"}[$interval]) or irate(mysql_global_status_opened_files{instance=\"$host\"}[5m])", "interval": "$interval", "intervalFactor": 1, @@ -3709,9 +3207,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL File Openings", @@ -3727,9 +3223,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3753,9 +3247,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3764,9 +3256,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -3789,29 +3279,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_open_files{instance=\"$host\"}", "interval": "$interval", "intervalFactor": 1, @@ -3822,12 +3304,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_open_files_limit{instance=\"$host\"}", "interval": "$interval", "intervalFactor": 1, @@ -3845,9 +3323,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Open Files", @@ -3863,9 +3339,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3897,17 +3371,13 @@ "y": 118 }, "id": 393, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Table Openings", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3917,9 +3387,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -3966,12 +3434,8 @@ "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_opened_tables{instance=\"$host\"}[$interval]) or irate(mysql_global_status_opened_tables{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -4018,9 +3482,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Table Open Cache Status", @@ -4036,9 +3498,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4062,9 +3522,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4074,9 +3532,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -4111,21 +3567,15 @@ "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_open_tables{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4137,12 +3587,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_table_open_cache{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4153,9 +3599,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Open Tables", @@ -4171,9 +3615,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4205,17 +3647,13 @@ "y": 126 }, "id": 394, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "MySQL Table Definition Cache", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4225,9 +3663,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -4274,12 +3710,8 @@ "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_open_table_definitions{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4291,12 +3723,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_table_definition_cache{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4316,9 +3744,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Table Definition Cache", @@ -4334,9 +3760,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4368,17 +3792,13 @@ "y": 134 }, "id": 395, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "System Charts", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4387,9 +3807,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -4413,29 +3831,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(node_vmstat_pgpgin{instance=\"$host\"}[$interval]) * 1024 or irate(node_vmstat_pgpgin{instance=\"$host\"}[5m]) * 1024", "format": "time_series", "interval": "$interval", @@ -4448,12 +3858,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(node_vmstat_pgpgout{instance=\"$host\"}[$interval]) * 1024 or irate(node_vmstat_pgpgout{instance=\"$host\"}[5m]) * 1024", "format": "time_series", "interval": "$interval", @@ -4465,9 +3871,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "I/O Activity", @@ -4484,9 +3888,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4511,9 +3913,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4522,9 +3922,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -4549,29 +3947,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": true, "steppedLine": false, "targets": [ { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "max(node_memory_MemTotal{instance=\"$host\"}) without(job) - \n(max(node_memory_MemFree{instance=\"$host\"}) without(job) + \nmax(node_memory_Buffers{instance=\"$host\"}) without(job) + \n(max(node_memory_Cached{instance=\"$host\",job=~\"rds-enhanced|linux\"}) without (job) or \nmax(node_memory_Cached{instance=\"$host\",job=\"rds-basic\"}) without (job)))", "format": "time_series", "interval": "$interval", @@ -4584,12 +3974,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "node_memory_MemFree{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4602,12 +3988,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "node_memory_Buffers{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4620,12 +4002,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "max(node_memory_Cached{instance=~\"$host\",job=~\"rds-enhanced|linux\"}) without (job) or \nmax(node_memory_Cached{instance=~\"$host\",job=~\"rds-basic\"}) without (job)", "format": "time_series", "interval": "$interval", @@ -4637,9 +4015,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Memory Distribution", @@ -4656,9 +4032,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4700,9 +4074,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -4728,9 +4100,7 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, @@ -4759,12 +4129,8 @@ "targets": [ { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "clamp_max(((avg by (mode) ( (clamp_max(rate(node_cpu{instance=\"$host\",mode!=\"idle\"}[$interval]),1)) or (clamp_max(irate(node_cpu{instance=\"$host\",mode!=\"idle\"}[5m]),1)) ))*100 or (avg_over_time(node_cpu_average{instance=~\"$host\", mode!=\"total\", mode!=\"idle\"}[$interval]) or avg_over_time(node_cpu_average{instance=~\"$host\", mode!=\"total\", mode!=\"idle\"}[5m]))),100)", "format": "time_series", "hide": false, @@ -4794,9 +4160,7 @@ "refId": "C" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "CPU Usage / Load", @@ -4812,9 +4176,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4840,9 +4202,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4851,9 +4211,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -4879,29 +4237,21 @@ }, "lines": false, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 1, "points": true, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "sum((rate(node_disk_read_time_ms{device!~\"dm-.+\", instance=\"$host\"}[$interval]) / rate(node_disk_reads_completed{device!~\"dm-.+\", instance=\"$host\"}[$interval])) or (irate(node_disk_read_time_ms{device!~\"dm-.+\", instance=\"$host\"}[5m]) / irate(node_disk_reads_completed{device!~\"dm-.+\", instance=\"$host\"}[5m]))\nor avg_over_time(aws_rds_read_latency_average{instance=\"$host\"}[$interval]) or avg_over_time(aws_rds_read_latency_average{instance=\"$host\"}[5m]))", "format": "time_series", "interval": "$interval", @@ -4914,12 +4264,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "sum((rate(node_disk_write_time_ms{device!~\"dm-.+\", instance=\"$host\"}[$interval]) / rate(node_disk_writes_completed{device!~\"dm-.+\", instance=\"$host\"}[$interval])) or (irate(node_disk_write_time_ms{device!~\"dm-.+\", instance=\"$host\"}[5m]) / irate(node_disk_writes_completed{device!~\"dm-.+\", instance=\"$host\"}[5m])) or \navg_over_time(aws_rds_write_latency_average{instance=\"$host\"}[$interval]) or avg_over_time(aws_rds_write_latency_average{instance=\"$host\"}[5m]))", "format": "time_series", "interval": "$interval", @@ -4931,9 +4277,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Disk Latency", @@ -4950,9 +4294,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4978,9 +4320,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4989,9 +4329,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -5016,9 +4354,7 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, @@ -5036,12 +4372,8 @@ "targets": [ { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "sum(rate(node_network_receive_bytes{instance=\"$host\", device!=\"lo\"}[$interval])) or sum(irate(node_network_receive_bytes{instance=\"$host\", device!=\"lo\"}[5m])) or sum(max_over_time(rdsosmetrics_network_rx{instance=\"$host\"}[$interval])) or sum(max_over_time(rdsosmetrics_network_rx{instance=\"$host\"}[5m])) ", "format": "time_series", "interval": "$interval", @@ -5054,12 +4386,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "sum(rate(node_network_transmit_bytes{instance=\"$host\", device!=\"lo\"}[$interval])) or sum(irate(node_network_transmit_bytes{instance=\"$host\", device!=\"lo\"}[5m])) or\nsum(max_over_time(rdsosmetrics_network_tx{instance=\"$host\"}[$interval])) or sum(max_over_time(rdsosmetrics_network_tx{instance=\"$host\"}[5m]))", "format": "time_series", "interval": "$interval", @@ -5071,9 +4399,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Network Traffic", @@ -5090,9 +4416,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -5117,9 +4441,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -5128,9 +4450,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -5154,29 +4474,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(node_vmstat_pswpin{instance=\"$host\"}[$interval]) * 4096 or irate(node_vmstat_pswpin{instance=\"$host\"}[5m]) * 4096", "format": "time_series", "interval": "$interval", @@ -5189,12 +4501,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(node_vmstat_pswpout{instance=\"$host\"}[$interval]) * 4096 or irate(node_vmstat_pswpout{instance=\"$host\"}[5m]) * 4096", "format": "time_series", "interval": "$interval", @@ -5206,9 +4514,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Swap Activity", @@ -5225,9 +4531,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -5254,10 +4558,7 @@ ], "refresh": "1m", "schemaVersion": 16, - "tags": [ - "Percona", - "MySQL" - ], + "tags": ["Percona", "MySQL"], "templating": { "list": [ { @@ -5325,9 +4626,7 @@ { "allFormat": "glob", "allValue": null, - "current": { - - }, + "current": {}, "datasource": "Prometheus", "hide": 0, "includeAll": false, @@ -5335,18 +4634,14 @@ "multi": false, "multiFormat": "regex values", "name": "host", - "options": [ - - ], + "options": [], "query": "label_values(mysql_up, instance)", "refresh": 1, "refresh_on_load": false, "regex": "", "sort": 1, "tagValuesQuery": null, - "tags": [ - - ], + "tags": [], "tagsQuery": null, "type": "query", "useTags": false @@ -5363,18 +4658,7 @@ "hidden": false, "notice": false, "now": true, - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], "status": "Stable", "type": "timepicker" }, @@ -5382,4 +4666,4 @@ "title": "MySQL Overview", "uid": "MQWgroiiz", "version": 1 -} \ No newline at end of file +} diff --git a/devenv/docker/ha_test/README.md b/devenv/docker/ha_test/README.md index 7a8b9a7d055..b18b0f1acbf 100644 --- a/devenv/docker/ha_test/README.md +++ b/devenv/docker/ha_test/README.md @@ -53,6 +53,7 @@ $ docker-compose up -d ``` Browse + - http://grafana.loc/ - http://prometheus.loc/ diff --git a/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json b/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json index 0357c3d10f1..822052ac4ab 100644 --- a/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json +++ b/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json @@ -140,21 +140,10 @@ "to": "now" }, "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] }, "timezone": "", "title": "Overview", "uid": "xHy7-hAik", "version": 6 -} \ No newline at end of file +} diff --git a/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json b/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json index 2bf789366cc..204c8b392d6 100644 --- a/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json +++ b/devenv/docker/ha_test/grafana/provisioning/dashboards/mysql/overview.json @@ -10,9 +10,7 @@ "limit": 100, "name": "PMM Annotations", "showIn": 0, - "tags": [ - "pmm_annotation" - ], + "tags": ["pmm_annotation"], "type": "tags" }, { @@ -24,9 +22,7 @@ "limit": 100, "name": "Annotations & Alerts", "showIn": 0, - "tags": [ - - ], + "tags": [], "type": "dashboard" } ] @@ -41,9 +37,7 @@ "icon": "dashboard", "includeVars": true, "keepTime": true, - "tags": [ - "QAN" - ], + "tags": ["QAN"], "targetBlank": false, "title": "Query Analytics", "type": "link", @@ -53,9 +47,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "OS" - ], + "tags": ["OS"], "targetBlank": false, "title": "OS", "type": "dashboards" @@ -64,9 +56,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "MySQL" - ], + "tags": ["MySQL"], "targetBlank": false, "title": "MySQL", "type": "dashboards" @@ -75,9 +65,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "MongoDB" - ], + "tags": ["MongoDB"], "targetBlank": false, "title": "MongoDB", "type": "dashboards" @@ -86,9 +74,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "PostgreSQL" - ], + "tags": ["PostgreSQL"], "targetBlank": false, "title": "PostgreSQL", "type": "dashboards" @@ -97,9 +83,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "HA" - ], + "tags": ["HA"], "targetBlank": false, "title": "HA", "type": "dashboards" @@ -108,9 +92,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "Cloud" - ], + "tags": ["Cloud"], "targetBlank": false, "title": "Cloud", "type": "dashboards" @@ -119,9 +101,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "Insight" - ], + "tags": ["Insight"], "targetBlank": false, "title": "Insight", "type": "dashboards" @@ -130,9 +110,7 @@ "asDropdown": true, "includeVars": true, "keepTime": true, - "tags": [ - "PMM" - ], + "tags": ["PMM"], "targetBlank": false, "title": "PMM", "type": "dashboards" @@ -148,9 +126,7 @@ "y": 0 }, "id": 382, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "", "type": "row" @@ -159,11 +135,7 @@ "cacheTimeout": null, "colorBackground": false, "colorValue": true, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], "datasource": "Prometheus", "decimals": 1, "description": "**MySQL Uptime**\n\nThe amount of time since the last restart of the MySQL server process.", @@ -186,9 +158,7 @@ "height": "125px", "id": 12, "interval": "$interval", - "links": [ - - ], + "links": [], "mappingType": 1, "mappingTypes": [ { @@ -224,12 +194,8 @@ "targets": [ { "calculatedInterval": "10m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_uptime{instance=\"$host\"}", "format": "time_series", "interval": "5m", @@ -245,20 +211,14 @@ "transparent": false, "type": "singlestat", "valueFontSize": "80%", - "valueMaps": [ - - ], + "valueMaps": [], "valueName": "current" }, { "cacheTimeout": null, "colorBackground": false, "colorValue": false, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], "datasource": "Prometheus", "decimals": 2, "description": "**Current QPS**\n\nBased on the queries reported by MySQL's ``SHOW STATUS`` command, it is the number of statements executed by the server within the last second. This variable includes statements executed within stored programs, unlike the Questions variable. It does not count \n``COM_PING`` or ``COM_STATISTICS`` commands.", @@ -324,12 +284,8 @@ "targets": [ { "calculatedInterval": "10m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_queries{instance=\"$host\"}[$interval]) or irate(mysql_global_status_queries{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -345,20 +301,14 @@ "transparent": false, "type": "singlestat", "valueFontSize": "80%", - "valueMaps": [ - - ], + "valueMaps": [], "valueName": "current" }, { "cacheTimeout": null, "colorBackground": false, "colorValue": false, - "colors": [ - "rgba(50, 172, 45, 0.97)", - "rgba(237, 129, 40, 0.89)", - "rgba(245, 54, 54, 0.9)" - ], + "colors": ["rgba(50, 172, 45, 0.97)", "rgba(237, 129, 40, 0.89)", "rgba(245, 54, 54, 0.9)"], "datasource": "Prometheus", "decimals": 0, "description": "**InnoDB Buffer Pool Size**\n\nInnoDB maintains a storage area called the buffer pool for caching data and indexes in memory. Knowing how the InnoDB buffer pool works, and taking advantage of it to keep frequently accessed data in memory, is one of the most important aspects of MySQL tuning. The goal is to keep the working set in memory. In most cases, this should be between 60%-90% of available memory on a dedicated database host, but depends on many factors.", @@ -424,12 +374,8 @@ "targets": [ { "calculatedInterval": "10m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_innodb_buffer_pool_size{instance=\"$host\"}", "format": "time_series", "interval": "5m", @@ -445,20 +391,14 @@ "transparent": false, "type": "singlestat", "valueFontSize": "80%", - "valueMaps": [ - - ], + "valueMaps": [], "valueName": "current" }, { "cacheTimeout": null, "colorBackground": false, "colorValue": true, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], "datasource": "Prometheus", "decimals": 0, "description": "**InnoDB Buffer Pool Size % of Total RAM**\n\nInnoDB maintains a storage area called the buffer pool for caching data and indexes in memory. Knowing how the InnoDB buffer pool works, and taking advantage of it to keep frequently accessed data in memory, is one of the most important aspects of MySQL tuning. The goal is to keep the working set in memory. In most cases, this should be between 60%-90% of available memory on a dedicated database host, but depends on many factors.", @@ -527,12 +467,8 @@ "targets": [ { "calculatedInterval": "10m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "(mysql_global_variables_innodb_buffer_pool_size{instance=\"$host\"} * 100) / on (instance) node_memory_MemTotal{instance=\"$host\"}", "format": "time_series", "interval": "5m", @@ -548,9 +484,7 @@ "transparent": false, "type": "singlestat", "valueFontSize": "80%", - "valueMaps": [ - - ], + "valueMaps": [], "valueName": "current" }, { @@ -562,17 +496,13 @@ "y": 3 }, "id": 383, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Connections", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -582,9 +512,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -632,12 +560,8 @@ "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "max(max_over_time(mysql_global_status_threads_connected{instance=\"$host\"}[$interval]) or mysql_global_status_threads_connected{instance=\"$host\"} )", "format": "time_series", "interval": "$interval", @@ -649,12 +573,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_max_used_connections{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -667,12 +587,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_max_connections{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -684,9 +600,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Connections", @@ -702,9 +616,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -730,9 +642,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -742,9 +652,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -767,9 +675,7 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, @@ -798,12 +704,8 @@ "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "max_over_time(mysql_global_status_threads_connected{instance=\"$host\"}[$interval]) or\nmax_over_time(mysql_global_status_threads_connected{instance=\"$host\"}[5m])", "format": "time_series", "hide": false, @@ -816,12 +718,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "max_over_time(mysql_global_status_threads_running{instance=\"$host\"}[$interval]) or\nmax_over_time(mysql_global_status_threads_running{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -841,9 +739,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Client Thread Activity", @@ -859,9 +755,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - "total" - ] + "values": ["total"] }, "yaxes": [ { @@ -895,17 +789,13 @@ "y": 11 }, "id": 384, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Table Locks", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -915,9 +805,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -953,21 +841,15 @@ "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_questions{instance=\"$host\"}[$interval]) or irate(mysql_global_status_questions{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -978,9 +860,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Questions", @@ -996,9 +876,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1022,9 +900,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1034,9 +910,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1083,12 +957,8 @@ "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_thread_cache_size{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -1100,12 +970,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_threads_cached{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -1117,12 +983,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_threads_created{instance=\"$host\"}[$interval]) or irate(mysql_global_status_threads_created{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1133,9 +995,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Thread Cache", @@ -1152,9 +1012,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1186,17 +1044,13 @@ "y": 19 }, "id": 385, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Temporary Objects", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1205,9 +1059,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1230,29 +1082,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_created_tmp_tables{instance=\"$host\"}[$interval]) or irate(mysql_global_status_created_tmp_tables{instance=\"$host\"}[5m])", "interval": "$interval", "intervalFactor": 1, @@ -1263,12 +1107,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_created_tmp_disk_tables{instance=\"$host\"}[$interval]) or irate(mysql_global_status_created_tmp_disk_tables{instance=\"$host\"}[5m])", "interval": "$interval", "intervalFactor": 1, @@ -1279,12 +1119,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_created_tmp_files{instance=\"$host\"}[$interval]) or irate(mysql_global_status_created_tmp_files{instance=\"$host\"}[5m])", "interval": "$interval", "intervalFactor": 1, @@ -1294,9 +1130,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Temporary Objects", @@ -1312,9 +1146,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1338,9 +1170,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1350,9 +1180,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1377,29 +1205,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_select_full_join{instance=\"$host\"}[$interval]) or irate(mysql_global_status_select_full_join{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1411,12 +1231,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_select_full_range_join{instance=\"$host\"}[$interval]) or irate(mysql_global_status_select_full_range_join{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1428,12 +1244,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_select_range{instance=\"$host\"}[$interval]) or irate(mysql_global_status_select_range{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1445,12 +1257,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_select_range_check{instance=\"$host\"}[$interval]) or irate(mysql_global_status_select_range_check{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1462,12 +1270,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_select_scan{instance=\"$host\"}[$interval]) or irate(mysql_global_status_select_scan{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1478,9 +1282,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Select Types", @@ -1496,9 +1298,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1530,17 +1330,13 @@ "y": 27 }, "id": 386, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Sorts", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1550,9 +1346,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1576,29 +1370,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_sort_rows{instance=\"$host\"}[$interval]) or irate(mysql_global_status_sort_rows{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1610,12 +1396,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_sort_range{instance=\"$host\"}[$interval]) or irate(mysql_global_status_sort_range{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1627,12 +1409,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_sort_merge_passes{instance=\"$host\"}[$interval]) or irate(mysql_global_status_sort_merge_passes{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1644,12 +1422,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_sort_scan{instance=\"$host\"}[$interval]) or irate(mysql_global_status_sort_scan{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1660,9 +1434,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Sorts", @@ -1678,9 +1450,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1704,9 +1474,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1716,9 +1484,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1740,29 +1506,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_slow_queries{instance=\"$host\"}[$interval]) or irate(mysql_global_status_slow_queries{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1773,9 +1531,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Slow Queries", @@ -1791,9 +1547,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1827,17 +1581,13 @@ "y": 35 }, "id": 387, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Aborted", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1847,9 +1597,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -1871,29 +1619,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_aborted_connects{instance=\"$host\"}[$interval]) or irate(mysql_global_status_aborted_connects{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1905,12 +1645,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_aborted_clients{instance=\"$host\"}[$interval]) or irate(mysql_global_status_aborted_clients{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -1922,9 +1658,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Aborted Connections", @@ -1940,9 +1674,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -1968,9 +1700,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -1980,9 +1710,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -2005,29 +1733,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_table_locks_immediate{instance=\"$host\"}[$interval]) or irate(mysql_global_status_table_locks_immediate{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -2039,12 +1759,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_table_locks_waited{instance=\"$host\"}[$interval]) or irate(mysql_global_status_table_locks_waited{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -2055,9 +1771,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Table Locks", @@ -2073,9 +1787,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2107,17 +1819,13 @@ "y": 43 }, "id": 388, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Network", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -2127,9 +1835,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -2152,29 +1858,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": true, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_bytes_received{instance=\"$host\"}[$interval]) or irate(mysql_global_status_bytes_received{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -2186,12 +1884,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_bytes_sent{instance=\"$host\"}[$interval]) or irate(mysql_global_status_bytes_sent{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -2202,9 +1896,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Network Traffic", @@ -2220,9 +1912,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2246,9 +1936,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": true, "dashLength": 10, "dashes": false, @@ -2258,9 +1946,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -2284,29 +1970,21 @@ }, "lines": false, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": true, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "increase(mysql_global_status_bytes_received{instance=\"$host\"}[1h])", "format": "time_series", "interval": "1h", @@ -2318,12 +1996,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "increase(mysql_global_status_bytes_sent{instance=\"$host\"}[1h])", "format": "time_series", "interval": "1h", @@ -2334,9 +2008,7 @@ "step": 3600 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": "24h", "timeShift": null, "title": "MySQL Network Usage Hourly", @@ -2352,9 +2024,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2386,17 +2056,13 @@ "y": 51 }, "id": 389, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Memory", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -2406,9 +2072,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -2538,9 +2202,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Internal Memory Overview", @@ -2556,9 +2218,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2592,17 +2252,13 @@ "y": 59 }, "id": 390, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Command, Handlers, Processes", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -2612,9 +2268,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -2651,21 +2305,15 @@ "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "topk(5, rate(mysql_global_status_commands_total{instance=\"$host\"}[$interval])>0) or topk(5, irate(mysql_global_status_commands_total{instance=\"$host\"}[5m])>0)", "format": "time_series", "hide": false, @@ -2677,9 +2325,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Top Command Counters", @@ -2695,9 +2341,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2721,9 +2365,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": true, "dashLength": 10, "dashes": false, @@ -2733,9 +2375,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -2771,21 +2411,15 @@ "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": true, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "topk(5, increase(mysql_global_status_commands_total{instance=\"$host\"}[1h])>0)", "format": "time_series", "interval": "1h", @@ -2796,9 +2430,7 @@ "step": 3600 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": "24h", "timeShift": null, "title": "Top Command Counters Hourly", @@ -2814,9 +2446,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2840,9 +2470,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -2852,9 +2480,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -2878,29 +2504,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_handlers_total{instance=\"$host\", handler!~\"commit|rollback|savepoint.*|prepare\"}[$interval]) or irate(mysql_global_status_handlers_total{instance=\"$host\", handler!~\"commit|rollback|savepoint.*|prepare\"}[5m])", "format": "time_series", "interval": "$interval", @@ -2911,9 +2529,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Handlers", @@ -2929,9 +2545,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -2955,9 +2569,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -2966,9 +2578,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -2992,29 +2602,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_handlers_total{instance=\"$host\", handler=~\"commit|rollback|savepoint.*|prepare\"}[$interval]) or irate(mysql_global_status_handlers_total{instance=\"$host\", handler=~\"commit|rollback|savepoint.*|prepare\"}[5m])", "interval": "$interval", "intervalFactor": 1, @@ -3024,9 +2626,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Transaction Handlers", @@ -3042,9 +2642,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3068,9 +2666,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3079,9 +2675,7 @@ "editable": true, "error": false, "fill": 0, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -3105,29 +2699,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_info_schema_threads{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -3138,9 +2724,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Process States", @@ -3156,9 +2740,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3182,9 +2764,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": true, "dashLength": 10, "dashes": false, @@ -3193,9 +2773,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -3219,29 +2797,21 @@ }, "lines": false, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": true, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "topk(5, avg_over_time(mysql_info_schema_threads{instance=\"$host\"}[1h]))", "interval": "1h", "intervalFactor": 1, @@ -3251,9 +2821,7 @@ "step": 3600 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": "24h", "timeShift": null, "title": "Top Process States Hourly", @@ -3269,9 +2837,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3303,17 +2869,13 @@ "y": 102 }, "id": 391, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Query Cache", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3323,9 +2885,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -3348,29 +2908,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_qcache_free_memory{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -3382,12 +2934,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_query_cache_size{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -3398,9 +2946,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Query Cache Memory", @@ -3416,9 +2962,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3442,9 +2986,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3454,9 +2996,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -3480,29 +3020,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_qcache_hits{instance=\"$host\"}[$interval]) or irate(mysql_global_status_qcache_hits{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -3514,12 +3046,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_qcache_inserts{instance=\"$host\"}[$interval]) or irate(mysql_global_status_qcache_inserts{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -3531,12 +3059,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_qcache_not_cached{instance=\"$host\"}[$interval]) or irate(mysql_global_status_qcache_not_cached{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -3548,12 +3072,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_qcache_lowmem_prunes{instance=\"$host\"}[$interval]) or irate(mysql_global_status_qcache_lowmem_prunes{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -3565,12 +3085,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_qcache_queries_in_cache{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -3581,9 +3097,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Query Cache Activity", @@ -3599,9 +3113,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3633,17 +3145,13 @@ "y": 110 }, "id": 392, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Files and Tables", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3652,9 +3160,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -3677,29 +3183,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_opened_files{instance=\"$host\"}[$interval]) or irate(mysql_global_status_opened_files{instance=\"$host\"}[5m])", "interval": "$interval", "intervalFactor": 1, @@ -3709,9 +3207,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL File Openings", @@ -3727,9 +3223,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3753,9 +3247,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3764,9 +3256,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -3789,29 +3279,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_open_files{instance=\"$host\"}", "interval": "$interval", "intervalFactor": 1, @@ -3822,12 +3304,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_open_files_limit{instance=\"$host\"}", "interval": "$interval", "intervalFactor": 1, @@ -3845,9 +3323,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Open Files", @@ -3863,9 +3339,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -3897,17 +3371,13 @@ "y": 118 }, "id": 393, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "Table Openings", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -3917,9 +3387,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -3966,12 +3434,8 @@ "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(mysql_global_status_opened_tables{instance=\"$host\"}[$interval]) or irate(mysql_global_status_opened_tables{instance=\"$host\"}[5m])", "format": "time_series", "interval": "$interval", @@ -4018,9 +3482,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Table Open Cache Status", @@ -4036,9 +3498,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4062,9 +3522,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4074,9 +3532,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -4111,21 +3567,15 @@ "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_open_tables{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4137,12 +3587,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_table_open_cache{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4153,9 +3599,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Open Tables", @@ -4171,9 +3615,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4205,17 +3647,13 @@ "y": 126 }, "id": 394, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "MySQL Table Definition Cache", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4225,9 +3663,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 24, @@ -4274,12 +3710,8 @@ "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_status_open_table_definitions{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4291,12 +3723,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "mysql_global_variables_table_definition_cache{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4316,9 +3744,7 @@ "step": 20 } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "MySQL Table Definition Cache", @@ -4334,9 +3760,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4368,17 +3792,13 @@ "y": 134 }, "id": 395, - "panels": [ - - ], + "panels": [], "repeat": null, "title": "System Charts", "type": "row" }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4387,9 +3807,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -4413,29 +3831,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(node_vmstat_pgpgin{instance=\"$host\"}[$interval]) * 1024 or irate(node_vmstat_pgpgin{instance=\"$host\"}[5m]) * 1024", "format": "time_series", "interval": "$interval", @@ -4448,12 +3858,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(node_vmstat_pgpgout{instance=\"$host\"}[$interval]) * 1024 or irate(node_vmstat_pgpgout{instance=\"$host\"}[5m]) * 1024", "format": "time_series", "interval": "$interval", @@ -4465,9 +3871,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "I/O Activity", @@ -4484,9 +3888,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4511,9 +3913,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4522,9 +3922,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -4549,29 +3947,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": true, "steppedLine": false, "targets": [ { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "max(node_memory_MemTotal{instance=\"$host\"}) without(job) - \n(max(node_memory_MemFree{instance=\"$host\"}) without(job) + \nmax(node_memory_Buffers{instance=\"$host\"}) without(job) + \n(max(node_memory_Cached{instance=\"$host\",job=~\"rds-enhanced|linux\"}) without (job) or \nmax(node_memory_Cached{instance=\"$host\",job=\"rds-basic\"}) without (job)))", "format": "time_series", "interval": "$interval", @@ -4584,12 +3974,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "node_memory_MemFree{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4602,12 +3988,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "node_memory_Buffers{instance=\"$host\"}", "format": "time_series", "interval": "$interval", @@ -4620,12 +4002,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "max(node_memory_Cached{instance=~\"$host\",job=~\"rds-enhanced|linux\"}) without (job) or \nmax(node_memory_Cached{instance=~\"$host\",job=~\"rds-basic\"}) without (job)", "format": "time_series", "interval": "$interval", @@ -4637,9 +4015,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Memory Distribution", @@ -4656,9 +4032,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4700,9 +4074,7 @@ "editable": true, "error": false, "fill": 6, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -4728,9 +4100,7 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, @@ -4759,12 +4129,8 @@ "targets": [ { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "clamp_max(((avg by (mode) ( (clamp_max(rate(node_cpu{instance=\"$host\",mode!=\"idle\"}[$interval]),1)) or (clamp_max(irate(node_cpu{instance=\"$host\",mode!=\"idle\"}[5m]),1)) ))*100 or (avg_over_time(node_cpu_average{instance=~\"$host\", mode!=\"total\", mode!=\"idle\"}[$interval]) or avg_over_time(node_cpu_average{instance=~\"$host\", mode!=\"total\", mode!=\"idle\"}[5m]))),100)", "format": "time_series", "hide": false, @@ -4794,9 +4160,7 @@ "refId": "C" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "CPU Usage / Load", @@ -4812,9 +4176,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4840,9 +4202,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4851,9 +4211,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -4879,29 +4237,21 @@ }, "lines": false, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 1, "points": true, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "sum((rate(node_disk_read_time_ms{device!~\"dm-.+\", instance=\"$host\"}[$interval]) / rate(node_disk_reads_completed{device!~\"dm-.+\", instance=\"$host\"}[$interval])) or (irate(node_disk_read_time_ms{device!~\"dm-.+\", instance=\"$host\"}[5m]) / irate(node_disk_reads_completed{device!~\"dm-.+\", instance=\"$host\"}[5m]))\nor avg_over_time(aws_rds_read_latency_average{instance=\"$host\"}[$interval]) or avg_over_time(aws_rds_read_latency_average{instance=\"$host\"}[5m]))", "format": "time_series", "interval": "$interval", @@ -4914,12 +4264,8 @@ }, { "calculatedInterval": "2m", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "sum((rate(node_disk_write_time_ms{device!~\"dm-.+\", instance=\"$host\"}[$interval]) / rate(node_disk_writes_completed{device!~\"dm-.+\", instance=\"$host\"}[$interval])) or (irate(node_disk_write_time_ms{device!~\"dm-.+\", instance=\"$host\"}[5m]) / irate(node_disk_writes_completed{device!~\"dm-.+\", instance=\"$host\"}[5m])) or \navg_over_time(aws_rds_write_latency_average{instance=\"$host\"}[$interval]) or avg_over_time(aws_rds_write_latency_average{instance=\"$host\"}[5m]))", "format": "time_series", "interval": "$interval", @@ -4931,9 +4277,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Disk Latency", @@ -4950,9 +4294,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -4978,9 +4320,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -4989,9 +4329,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -5016,9 +4354,7 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, @@ -5036,12 +4372,8 @@ "targets": [ { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "sum(rate(node_network_receive_bytes{instance=\"$host\", device!=\"lo\"}[$interval])) or sum(irate(node_network_receive_bytes{instance=\"$host\", device!=\"lo\"}[5m])) or sum(max_over_time(rdsosmetrics_network_rx{instance=\"$host\"}[$interval])) or sum(max_over_time(rdsosmetrics_network_rx{instance=\"$host\"}[5m])) ", "format": "time_series", "interval": "$interval", @@ -5054,12 +4386,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "sum(rate(node_network_transmit_bytes{instance=\"$host\", device!=\"lo\"}[$interval])) or sum(irate(node_network_transmit_bytes{instance=\"$host\", device!=\"lo\"}[5m])) or\nsum(max_over_time(rdsosmetrics_network_tx{instance=\"$host\"}[$interval])) or sum(max_over_time(rdsosmetrics_network_tx{instance=\"$host\"}[5m]))", "format": "time_series", "interval": "$interval", @@ -5071,9 +4399,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Network Traffic", @@ -5090,9 +4416,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -5117,9 +4441,7 @@ } }, { - "aliasColors": { - - }, + "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, @@ -5128,9 +4450,7 @@ "editable": true, "error": false, "fill": 2, - "grid": { - - }, + "grid": {}, "gridPos": { "h": 7, "w": 12, @@ -5154,29 +4474,21 @@ }, "lines": true, "linewidth": 2, - "links": [ - - ], + "links": [], "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", - "seriesOverrides": [ - - ], + "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(node_vmstat_pswpin{instance=\"$host\"}[$interval]) * 4096 or irate(node_vmstat_pswpin{instance=\"$host\"}[5m]) * 4096", "format": "time_series", "interval": "$interval", @@ -5189,12 +4501,8 @@ }, { "calculatedInterval": "2s", - "datasourceErrors": { - - }, - "errors": { - - }, + "datasourceErrors": {}, + "errors": {}, "expr": "rate(node_vmstat_pswpout{instance=\"$host\"}[$interval]) * 4096 or irate(node_vmstat_pswpout{instance=\"$host\"}[5m]) * 4096", "format": "time_series", "interval": "$interval", @@ -5206,9 +4514,7 @@ "target": "" } ], - "thresholds": [ - - ], + "thresholds": [], "timeFrom": null, "timeShift": null, "title": "Swap Activity", @@ -5225,9 +4531,7 @@ "mode": "time", "name": null, "show": true, - "values": [ - - ] + "values": [] }, "yaxes": [ { @@ -5254,10 +4558,7 @@ ], "refresh": "1m", "schemaVersion": 16, - "tags": [ - "Percona", - "MySQL" - ], + "tags": ["Percona", "MySQL"], "templating": { "list": [ { @@ -5325,9 +4626,7 @@ { "allFormat": "glob", "allValue": null, - "current": { - - }, + "current": {}, "datasource": "Prometheus", "hide": 0, "includeAll": false, @@ -5335,18 +4634,14 @@ "multi": false, "multiFormat": "regex values", "name": "host", - "options": [ - - ], + "options": [], "query": "label_values(mysql_up, instance)", "refresh": 1, "refresh_on_load": false, "regex": "", "sort": 1, "tagValuesQuery": null, - "tags": [ - - ], + "tags": [], "tagsQuery": null, "type": "query", "useTags": false @@ -5363,18 +4658,7 @@ "hidden": false, "notice": false, "now": true, - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], "status": "Stable", "type": "timepicker" }, @@ -5382,4 +4666,4 @@ "title": "MySQL Overview", "uid": "MQWgroiiz", "version": 1 -} \ No newline at end of file +} diff --git a/devenv/docker/loadtest/README.md b/devenv/docker/loadtest/README.md index 34faf18d72f..92d55205093 100644 --- a/devenv/docker/loadtest/README.md +++ b/devenv/docker/loadtest/README.md @@ -59,7 +59,6 @@ Run API key test (option `-k` must be a valid `admin` API key) $ ./run.sh -c auth_token_test -k "" ``` - Example output: ```bash diff --git a/devenv/frontend-service/README.md b/devenv/frontend-service/README.md index 97725978e1a..e0a3491ac53 100644 --- a/devenv/frontend-service/README.md +++ b/devenv/frontend-service/README.md @@ -2,16 +2,16 @@ This directory contains a docker compose + Tilt setup for running a full frontend service stack locally. It contains: - - frontend-service - - backend api - - static asset cdn. +- frontend-service +- backend api +- static asset cdn. ## Getting started On top of the main Grafana development dependencies, you will need installed: - - [Docker](https://docs.docker.com/get-started/get-docker/) - - [Tilt](https://docs.tilt.dev/install.html). At the moment we're not using Kubernetes locally, so you shouldn't need to follow the instructions to install kubectl or kind. +- [Docker](https://docs.docker.com/get-started/get-docker/) +- [Tilt](https://docs.tilt.dev/install.html). At the moment we're not using Kubernetes locally, so you shouldn't need to follow the instructions to install kubectl or kind. To start the stack, from the root of the Grafana project run `make frontend-service-up`. Tilt will orchestrate the webpack and docker builds, and then run the services with Docker compose. You can monitor it's progress and see logs with the URL to the Tilt console. Once done, you can access Grafana at `http://localhost:3000`. @@ -21,8 +21,8 @@ Quitting the process will not stop the service from running. Run `make frontend- To simulate the `/bootdata` endpoint being available, there are special control URLs you can visit that use cookies to control behaviour: - - `/-/down` - Simulates the endpoint being unavailable for 60 seconds. - - `/-/down/:seconds` - Simulates the endpoint being unavailable for a custom number of seconds. - - `/-/up` - Restores the endpoint to being available. +- `/-/down` - Simulates the endpoint being unavailable for 60 seconds. +- `/-/down/:seconds` - Simulates the endpoint being unavailable for a custom number of seconds. +- `/-/up` - Restores the endpoint to being available. When unavailable, the API will return `HTTP 503 Service Unavailable` with a JSON payload. diff --git a/devenv/frontend-service/Tiltfile b/devenv/frontend-service/Tiltfile index f4d3887df3b..b7e6f81eca3 100644 --- a/devenv/frontend-service/Tiltfile +++ b/devenv/frontend-service/Tiltfile @@ -79,6 +79,7 @@ docker_build('grafana-fs-dev', # Paths relative to the docker context (root of the repo) only=[ 'devenv/frontend-service/build/grafana', + 'devenv/frontend-service/provisioning', 'conf/defaults.ini', 'public/emails', 'public/views', @@ -96,6 +97,7 @@ docker_build('grafana-fs-dev', sync('../../public/dashboards', '/grafana/public/dashboards'), sync('../../public/app/plugins', '/grafana/public/app/plugins'), sync('../../public/build/assets-manifest.json', '/grafana/public/build/assets-manifest.json'), + sync('./provisioning', '/ignore/provisioning'), # Just to trigger a restart instead of rebuild restart_container() ] ) diff --git a/devenv/frontend-service/docker-compose.yaml b/devenv/frontend-service/docker-compose.yaml index 1d5c46ba546..17a0ab3c7ef 100644 --- a/devenv/frontend-service/docker-compose.yaml +++ b/devenv/frontend-service/docker-compose.yaml @@ -21,6 +21,9 @@ services: entrypoint: ['bin/grafana', 'server'] volumes: - backend-data:/grafana/data + - ./provisioning/datasources:/grafana/conf/provisioning/datasources + - ./provisioning/dashboards:/grafana/conf/provisioning/dashboards + - ../dev-dashboards:/grafana/conf/dev-dashboards environment: GF_SERVER_CDN_URL: http://localhost:3010 GF_FEATURE_TOGGLES_ENABLE: multiTenantFrontend diff --git a/devenv/frontend-service/provisioning/dashboards/devenv.yaml b/devenv/frontend-service/provisioning/dashboards/devenv.yaml new file mode 100644 index 00000000000..c22f2d90141 --- /dev/null +++ b/devenv/frontend-service/provisioning/dashboards/devenv.yaml @@ -0,0 +1,13 @@ +apiVersion: 1 + +providers: + - name: 'gdev dashboards' + folder: 'gdev dashboards' + folderUid: '' + type: file + allowUiUpdates: false + updateIntervalSeconds: 3600 + options: + # This path is relative to the WORKDIR in the docker container. It is mounted + # in the docker-compose.yml file. + path: conf/dev-dashboards diff --git a/devenv/frontend-service/provisioning/datasources/default.yaml b/devenv/frontend-service/provisioning/datasources/default.yaml new file mode 100644 index 00000000000..a3e3c50f570 --- /dev/null +++ b/devenv/frontend-service/provisioning/datasources/default.yaml @@ -0,0 +1,9 @@ +apiVersion: 1 + +prune: true + +datasources: + # From devenv/datasources.yaml + - name: Testdata + uid: PD8C576611E62080A + type: grafana-testdata-datasource diff --git a/devenv/jsonnet/dev-dashboards.libsonnet b/devenv/jsonnet/dev-dashboards.libsonnet index d16f9b96c84..c050297d430 100644 --- a/devenv/jsonnet/dev-dashboards.libsonnet +++ b/devenv/jsonnet/dev-dashboards.libsonnet @@ -94,6 +94,7 @@ "shared_queries": (import '../dev-dashboards/panel-common/shared_queries.json'), "slow_queries_and_annotations": (import '../dev-dashboards/scenarios/slow_queries_and_annotations.json'), "table_kitchen_sink": (import '../dev-dashboards/panel-table/table_kitchen_sink.json'), + "table_markdown": (import '../dev-dashboards/panel-table/table_markdown.json'), "table_pagination": (import '../dev-dashboards/panel-table/table_pagination.json'), "table_sparkline_cell": (import '../dev-dashboards/panel-table/table_sparkline_cell.json'), "table_tests": (import '../dev-dashboards/panel-table/table_tests.json'), diff --git a/docs/sources/administration/migration-guide/manually-migrate-to-grafana-cloud.md b/docs/sources/administration/migration-guide/manually-migrate-to-grafana-cloud.md index e68083eb4a0..5f4fa75a787 100644 --- a/docs/sources/administration/migration-guide/manually-migrate-to-grafana-cloud.md +++ b/docs/sources/administration/migration-guide/manually-migrate-to-grafana-cloud.md @@ -290,7 +290,7 @@ The following customizations are available via support: - Enabling [feature toggles](http://www.grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/feature-toggles). - [Single sign-on and team sync using SAML, LDAP, or OAuth](http://www.grafana.com/docs/grafana/latest/setup-grafana/configure-security/configure-authentication). - Enable [embedding Grafana dashboards in other applications](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#allow_embedding) for Grafana Cloud contracted customers. -- [Audit logging](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/audit-grafana/) ([Usage insights logs and dashboards](https://grafana.com/docs/grafana-cloud/account-management/usage-insights/) are available in Grafana Cloud Pro and Advanced by default). +- [Audit logging](https://grafana.com/docs/grafana/latest/setup-grafana/configure-security/audit-grafana/) ([Usage insights logs and dashboards](https://grafana.com/docs/grafana-cloud/account-management/usage-insights/) are available in select Grafana Cloud paid accounts). Note that the following custom configurations are not supported in Grafana Cloud: diff --git a/docs/sources/alerting/alerting-rules/alerting-migration.md b/docs/sources/alerting/alerting-rules/alerting-migration.md index 328ca0eefb7..2e63db00136 100644 --- a/docs/sources/alerting/alerting-rules/alerting-migration.md +++ b/docs/sources/alerting/alerting-rules/alerting-migration.md @@ -281,12 +281,13 @@ The `POST` endpoints can be used to import data source–managed alert rules. Th | POST | `/convert/prometheus/config/v1/rules` | [Create or update multiple rule groups](#create-or-update-multiple-rule-groups) across multiple namespaces. Requires [`X-Grafana-Alerting-Datasource-UID`](#x-grafana-alerting-datasource-uid). | None | | POST | `/convert/prometheus/config/v1/rules/:namespaceTitle` | Create or update a single rule group in a namespace. Requires [`X-Grafana-Alerting-Datasource-UID`](#x-grafana-alerting-datasource-uid). | [Set rule group](/docs/mimir/latest/references/http-api/#set-rule-group) | -The `GET` and `DELETE` endpoints work only with provisioned and imported alert rules. +The `GET` and `DELETE` endpoints work only with provisioned and imported alert rules. All `GET` endpoints support both JSON and YAML response formats based on the `Accept` header: use `application/json` for JSON responses, or `application/yaml` for YAML responses. YAML is the default format when no `Accept` header is specified. | Endpoint | Method | Summary | Mimir equivalent | | -------- | ------------------------------------------------------------ | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | GET | `/convert/prometheus/config/v1/rules` | Get all imported rule groups across all namespaces. | [List rule groups](/docs/mimir/latest/references/http-api/#list-rule-groups) | | GET | `/convert/prometheus/config/v1/rules/:namespaceTitle` | Get imported rule groups in a specific namespace. | [Get rule groups by namespace](/docs/mimir/latest/references/http-api/#get-rule-groups-by-namespace) | +| GET | `/convert/prometheus/config/v1/rules/:namespaceTitle/:group` | Get imported rule group in a specific namespace. | [Get rule group](/docs/mimir/latest/references/http-api/#get-rule-group) | | DELETE | `/convert/prometheus/config/v1/rules/:namespaceTitle` | Delete all imported alert rules in a namespace. | [Delete namespace](/docs/mimir/latest/references/http-api/#delete-namespace) | | DELETE | `/convert/prometheus/config/v1/rules/:namespaceTitle/:group` | Delete a specific imported rule group. | [Delete rule group](/docs/mimir/latest/references/http-api/#delete-rule-group) | diff --git a/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md b/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md index df7abac34b0..f7d1214271a 100644 --- a/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md +++ b/docs/sources/alerting/alerting-rules/create-grafana-managed-rule.md @@ -161,7 +161,7 @@ You can find the public data sources that support alert rules in the [Grafana Pl In Grafana Cloud, the number of Grafana-managed alert rules you can create depends on your Grafana Cloud plan. - Free Forever plan: You can create up to 100 free alert rules, with each alert rule having a maximum of 1000 alert instances. -- All paid plans (Pro and Advanced): They have a soft limit of 2000 alert rules and support unlimited alert instances. To increase the limit, open a support ticket from the [Cloud portal](/docs/grafana-cloud/account-management/support/). +- All paid plans: They have a soft limit of 2000 alert rules and support unlimited alert instances. To increase the limit, open a support ticket from the [Cloud portal](/docs/grafana-cloud/account-management/support/). ### Permissions diff --git a/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md b/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md index 052770cc058..8be72c73270 100644 --- a/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md +++ b/docs/sources/alerting/alerting-rules/link-alert-rules-to-panels.md @@ -67,6 +67,8 @@ By default, notification messages include a link to the dashboard panel. Additio ## Create alert rules from panels +{{< shared id="create-alert-from-panel" >}} + To streamline alert creation, you can create an alert rule directly from a panel. 1. Navigate to a dashboard in the **Dashboards** section. @@ -77,6 +79,8 @@ To streamline alert creation, you can create an alert rule directly from a panel - Sets the alert rule query using the panel query. 1. Complete the alert rule configuration and click **Save rule** to initiate the alert rule. +{{< /shared >}} + You can then [view the alert state on the panel](ref:view-alert-state-on-panels). By default, notification messages include a link to the dashboard panel. Additionally, you can [enable displaying panel screenshots in notifications](ref:images-in-notifications). diff --git a/docs/sources/alerting/best-practices/_index.md b/docs/sources/alerting/best-practices/_index.md index 18bcf13121a..41251a25994 100644 --- a/docs/sources/alerting/best-practices/_index.md +++ b/docs/sources/alerting/best-practices/_index.md @@ -21,6 +21,8 @@ This section provides a set of guides and examples of best practices for Grafana Designing and configuring an alert management set up that works takes time. Here are some additional tips on how to create an effective alert management set up: +{{< shared id="alert-planning-fundamentals" >}} + **Which are the key metrics for your business that you want to monitor and alert on?** - Find events that are important to know about and not so trivial or frequent that recipients ignore them. @@ -44,3 +46,5 @@ Designing and configuring an alert management set up that works takes time. Here - Avoid noisy, unnecessary alerts by using silences, mute timings, or pausing alert rule evaluation. - Continually tune your alert rules to review effectiveness. Remove alert rules to avoid duplication or ineffective alerts. - Continually review your thresholds and evaluation rules. + +{{< /shared >}} diff --git a/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md b/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md index 80a58728bc1..7ff256ac103 100644 --- a/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md +++ b/docs/sources/alerting/fundamentals/alert-rule-evaluation/_index.md @@ -81,30 +81,40 @@ Alert instances are routed for [notifications](ref:notifications) in two scenari ## Evaluation group -Every alert rule and recording rule is assigned to an evaluation group. +{{< shared id="evaluation-group-basics" >}} -Each evaluation group contains an **evaluation interval** that determines how frequently the rule is checked. For instance, the evaluation may occur every `10s`, `30s`, `1m`, `10m`, etc. +Every alert rule and recording rule is assigned to an evaluation group. Each evaluation group contains an **evaluation interval** that determines how frequently the rule is checked. For instance, the evaluation may occur every `10s`, `30s`, `1m`, `10m`, etc. + +{{< /shared >}} Rules can be evaluated concurrently or sequentially. For details, see [How rules are evaluated within a group](ref:evaluation-within-a-group). ## Pending period +{{< shared id="pending-period-basics" >}} + You can set a **Pending period** to prevent unnecessary notifications caused by temporary issues. When the alert condition is met, the alert instance enters the **Pending** state. It remains in this state until the condition has been continuously true for the entire **Pending period**. This ensures the condition breach is stable before the alert transitions to the **Alerting** state and routed for notification. +{{< /shared >}} + - **Normal** -> **Pending** -> **Alerting**\* You can also set the **Pending period** to zero to skip the **Pending** state entirely and transition to **Alerting** immediately. ## Keep firing for +{{< shared id="keep-firing-for" >}} + You can set a **Keep firing for** period to avoid repeated firing-resolving-firing notifications caused by flapping conditions. When the alert condition is no longer met during the **Alerting** state, the alert instance enters the **Recovering** state. +{{< /shared >}} + - **Alerting** → **Recovering** → **Normal (Resolved)**\* - After the **Keep firing for** period elapses, the alert transitions to the **Normal** state and is marked as **Resolved**. - If the alert condition is met again, the alert transitions back to the **Alerting** state, and no new notifications are sent. diff --git a/docs/sources/alerting/fundamentals/alert-rules/annotation-label.md b/docs/sources/alerting/fundamentals/alert-rules/annotation-label.md index 8d0aca31034..27ae61e3cb9 100644 --- a/docs/sources/alerting/fundamentals/alert-rules/annotation-label.md +++ b/docs/sources/alerting/fundamentals/alert-rules/annotation-label.md @@ -62,10 +62,14 @@ Labels and annotations add additional information about an alert using key/value ## Labels +{{< shared id="labels-basics" >}} + **Labels** are unique identifiers of an [alert instance](ref:alert-instances). You can use them for searching, silencing, and routing notifications. Examples of labels are `server=server1` or `team=backend`. Each alert rule can have more than one label and the complete set of labels for an alert rule is called its label set. It is this label set that identifies the alert. +{{< /shared >}} + For example, an alert instance might have the label set `{alertname="High CPU usage",server="server1"}` while another alert instance might have the label set `{alertname="High CPU usage",server="server2"}`. These are two separate alert instances because although their `alertname` labels are the same, their `server` labels are different. {{< figure alt="Image shows an example of an alert instance and the labels used on the alert instance." src="/static/img/docs/alerting/unified/multi-dimensional-alert.png" >}} @@ -134,6 +138,8 @@ If multiple label keys are sanitized to the same value, the duplicates have a sh ## Annotations +{{< shared id="annotations-basics" >}} + Annotations add additional information to alert instances, helping responders identify and address potential issues. Create clear and self-explanatory annotations so that first responders can investigate without needing deeper knowledge of the alert setup. @@ -145,6 +151,8 @@ Annotations are displayed in Grafana and are included by default in notification - `runbook_url`: The runbook page to guide operators managing a potential incident. - `__dashboardUid__` and `__panelId__`: [Link the alert to a dashboard and panel](ref:link-alert-rules-to-panels) to facilitate alert investigation. +{{< /shared >}} + For example, you can edit the annotation `summary` to explain why the alert was triggered: ``` diff --git a/docs/sources/alerting/fundamentals/notifications/_index.md b/docs/sources/alerting/fundamentals/notifications/_index.md index 3a0599722d3..fd8a77448b8 100644 --- a/docs/sources/alerting/fundamentals/notifications/_index.md +++ b/docs/sources/alerting/fundamentals/notifications/_index.md @@ -83,10 +83,14 @@ Start defining your [contact points](ref:contact-points) to specify how to recei ### Contact points +{{< shared id="contact-points-fundamentals" >}} + [Contact points](ref:contact-points) contain the configuration for sending alert notifications, specifying destinations like email, Slack, IRM, webhooks, and their notification messages. A contact point is a list of integrations, each sending a message to a specific destination. +{{< /shared >}} + By default, notification messages include common alert details, such as the number of alerts, alert names, labels, annotations, and other alert information. You can also customize notification messages and use notification templates. First, create the contact point and test the notifications. Then, configure the alert rule to send its notifications to either a contact point or through Notification Policies. diff --git a/docs/sources/developers/http_api/dashboard.md b/docs/sources/developers/http_api/dashboard.md index b862fb7c4a6..f5456a22e77 100644 --- a/docs/sources/developers/http_api/dashboard.md +++ b/docs/sources/developers/http_api/dashboard.md @@ -568,8 +568,6 @@ Gets a dashboard via the dashboard uid. - namespace: to read more about the namespace to use, see the [API overview](https://grafana.com/docs/grafana//developers/http_api/apis/). - uid: the unique identifier of the dashboard to update. this will be the _name_ in the dashboard response -Note: For large dashboards, add `/dto` to the end of the URL to get the full dashboard body. - **Required permissions** See note in the [introduction]({{< ref "#dashboard-api" >}}) for an explanation. diff --git a/docs/sources/introduction/grafana-enterprise.md b/docs/sources/introduction/grafana-enterprise.md index 3993809d076..ef8884f3c55 100644 --- a/docs/sources/introduction/grafana-enterprise.md +++ b/docs/sources/introduction/grafana-enterprise.md @@ -19,7 +19,7 @@ To learn more about Grafana Enterprise, refer to [our product page](/enterprise) ## Enterprise features in Grafana Cloud -Many Grafana Enterprise features are also available in [Grafana Cloud](/docs/grafana-cloud) Free, Pro, and Advanced accounts. For details, refer to [Grafana Cloud pricing](/pricing/#featuresTable). +Many Grafana Enterprise features are also available in paid [Grafana Cloud](/docs/grafana-cloud) accounts. For details, refer to [Grafana Cloud features](/docs/grafana-cloud/introduction/understand-grafana-cloud-features/). For pricing and plans, refer to [Grafana Cloud pricing](https://grafana.com/pricing/). To migrate to Grafana Cloud, refer to [Migrate from Grafana Enterprise to Grafana Cloud](/docs/grafana//administration/migration-guide/) diff --git a/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md b/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md index ba94fe38671..bb8de19264f 100644 --- a/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md +++ b/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md @@ -1465,7 +1465,7 @@ For each generated **Trend** field value, a calculation function can be selected ### Transpose Use this transformation to pivot the data frame, converting rows into columns and columns into rows. This transformation is particularly useful when you want to switch the orientation of your data to better suit your visualization needs. -If you have multiple types it will default to string type. +If you have multiple types, it will default to string type. You can select how empty cells should be represented. **Before Transformation:** diff --git a/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md b/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md index 2fd80cbac5f..92004450844 100644 --- a/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md +++ b/docs/sources/setup-grafana/configure-grafana/configure-custom-branding/index.md @@ -15,10 +15,7 @@ weight: 300 Custom branding enables you to replace the Grafana Labs brand and logo with your corporate brand and logo. {{< admonition type="note" >}} -Available in [Grafana Enterprise](../../../introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). For Cloud Advanced and Enterprise customers, please provide custom elements and logos to our Support team. We will help you host your images and update your custom branding. - -This feature is not available for Grafana Free and Pro tiers. -For more information on feature availability across plans, refer to our [feature comparison page](/docs/grafana-cloud/cost-management-and-billing/understand-grafana-cloud-features/) +Available in [Grafana Enterprise](../../../introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. For Cloud customers, please provide custom elements and logos to our Support team. We will help you host your images and update your custom branding. {{< /admonition >}} diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/auth-proxy/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/auth-proxy/index.md index 3d1c4a48a6b..a379eb82d52 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/auth-proxy/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/auth-proxy/index.md @@ -237,7 +237,8 @@ If the user is deleted from Grafana, the user will be not be able to login and r ### Team Sync {{< admonition type="note" >}} -Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Advanced](https://grafana.com/docs/grafana-cloud/). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. + {{< /admonition >}} With Team Sync, it's possible to set up synchronization between teams in your authentication provider and Grafana. You can send Grafana values as part of an HTTP header and have Grafana map them to your team structure. This allows you to put users into specific teams automatically. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md index 69e6e1415f0..f3d2c5bea74 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/azuread/index.md @@ -414,7 +414,7 @@ auto_login = true ### Team Sync {{< admonition type="note" >}} -Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Advanced](https://grafana.com/docs/grafana-cloud/). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} With Team Sync you can map your Entra ID groups to teams in Grafana so that your users will automatically be added to diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md index 9ebe70e6e55..f28dc35b7f6 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/generic-oauth/index.md @@ -320,7 +320,7 @@ org_mapping = org_foo:org_foo:Viewer org_bar:org_bar:Editor *:org_baz:Editor ## Configure team synchronization {{< admonition type="note" >}} -Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Advanced](https://grafana.com/docs/grafana-cloud/). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} By using Team Sync, you can link your OAuth2 groups to teams within Grafana. This will automatically assign users to the appropriate teams. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md index 7a327576cfd..c11ef4fd3ab 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/github/index.md @@ -211,7 +211,7 @@ role_attribute_path = [login=='octocat'][0] && 'GrafanaAdmin' || 'Viewer' ## Configure team synchronization {{< admonition type="note" >}} -Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Advanced](https://grafana.com/docs/grafana-cloud/). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} By using Team Sync, you can map teams from your GitHub organization to teams within Grafana. This will automatically assign users to the appropriate teams. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md index 22363e59b54..ac45c634599 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/gitlab/index.md @@ -236,7 +236,7 @@ use_refresh_token = true ## Configure team synchronization {{< admonition type="note" >}} -Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Advanced](https://grafana.com/docs/grafana-cloud/). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} By using Team Sync, you can map GitLab groups to teams within Grafana. This will automatically assign users to the appropriate teams. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md index 59f2c6951a8..367d05c068d 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/google/index.md @@ -160,7 +160,7 @@ auto_login = true ### Configure team synchronization {{< admonition type="note" >}} -Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Advanced](https://grafana.com/docs/grafana-cloud/). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} With team sync, you can easily add users to teams by utilizing their Google groups. To set up team sync for Google OAuth, refer to the following example. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md index 88adc9d4a2a..6425eb09db7 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/keycloak/index.md @@ -109,7 +109,7 @@ viewer ## Team sync {{< admonition type="note" >}} -Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Advanced](https://grafana.com/docs/grafana-cloud/). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} [Teamsync](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-team-sync/) is a feature that allows you to map groups from your identity provider to Grafana teams. This is useful if you want to give your users access to specific dashboards or folders based on their group membership. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md index 8166760890f..53618823a06 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/okta/index.md @@ -236,7 +236,7 @@ org_mapping = ["Group 1:org_foo:Viewer", "Group 2:org_bar:Editor", "*:3:Editor"] ### Configure team synchronization {{< admonition type="note" >}} -Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Advanced](https://grafana.com/docs/grafana-cloud/). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} By using Team Sync, you can link your Okta groups to teams within Grafana. This will automatically assign users to the appropriate teams. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/saml/configure-saml-team-role-mapping/_index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/saml/configure-saml-team-role-mapping/_index.md index edfedcefb9a..ad4dab8cf00 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/saml/configure-saml-team-role-mapping/_index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/saml/configure-saml-team-role-mapping/_index.md @@ -12,7 +12,7 @@ weight: 540 # Configure team sync for SAML {{< admonition type="note" >}} -Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Advanced](https://grafana.com/docs/grafana-cloud/). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} To use SAML Team sync, set [`assertion_attribute_groups`](https://grafana.com/docs/grafana//setup-grafana/configure-grafana/enterprise-configuration/#assertion_attribute_groups) to the attribute name where you store user groups. Then Grafana will use attribute values extracted from SAML assertion to add user into the groups with the same name configured on the External group sync tab. diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/saml/saml-ui/_index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/saml/saml-ui/_index.md index 52d2c84fbfd..dee23439664 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/saml/saml-ui/_index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/saml/saml-ui/_index.md @@ -14,7 +14,7 @@ weight: 510 # Configure SAML authentication using the Grafana user interface {{< admonition type="note" >}} -Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) version 10.0 and later, and [Grafana Cloud Pro or Advanced](https://grafana.com/docs/grafana//introduction/grafana-cloud/). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) version 10.0 and later, and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} You can configure SAML authentication in Grafana through the user interface (UI) or the Grafana configuration file. For instructions on how to set up SAML using the Grafana configuration file, refer to [Configure SAML authentication using the configuration file](../#configure-saml-using-the-grafana-config-file). @@ -40,7 +40,7 @@ To follow this guide, you need: These permissions are granted by `fixed:authentication.config:writer` role. By default, this role is granted to Grafana server administrator in self-hosted instances and to Organization admins in Grafana Cloud instances. -- Grafana instance running Grafana version 10.0 or later with [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) or [Grafana Cloud Pro or Advanced](https://grafana.com/docs/grafana//introduction/grafana-cloud/) license. +- Grafana instance running Grafana version 10.0 or later with [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. ## Steps To Configure SAML Authentication diff --git a/docs/sources/setup-grafana/configure-security/configure-request-security.md b/docs/sources/setup-grafana/configure-security/configure-request-security.md index 8dca935e928..8164799a6d8 100644 --- a/docs/sources/setup-grafana/configure-security/configure-request-security.md +++ b/docs/sources/setup-grafana/configure-security/configure-request-security.md @@ -19,7 +19,7 @@ Request security allows you to limit requests from the Grafana server by targeti This can be used to limit access to internal systems that the server Grafana runs on can access but that users of Grafana should not be able to access. This feature does not affect traffic from the Grafana users browser. {{< admonition type="note" >}} -Available in [Grafana Enterprise](../../../introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). +Available in [Grafana Enterprise](../../../introduction/grafana-enterprise/) and to users on select Grafana Cloud account plans. For pricing information, visit our [pricing page](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} {{< admonition type="note" >}} diff --git a/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/_index.md b/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/_index.md index d3c2496f75b..f8e2ee47611 100644 --- a/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/_index.md +++ b/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/_index.md @@ -20,7 +20,7 @@ weight: 300 System for Cross-domain Identity Management (SCIM) is an open standard that allows automated user provisioning and management. With SCIM, you can automate the provisioning of users and groups from your identity provider to Grafana. {{< admonition type="note" >}} -Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/) in [public preview](https://grafana.com/docs/release-life-cycle/). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and select Grafana Cloud plans in [public preview](https://grafana.com/docs/release-life-cycle/). Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available. {{< /admonition >}} diff --git a/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/configure-scim-with-azuread/_index.md b/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/configure-scim-with-azuread/_index.md index 1554484f45b..bc04f80fa52 100644 --- a/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/configure-scim-with-azuread/_index.md +++ b/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/configure-scim-with-azuread/_index.md @@ -21,7 +21,7 @@ weight: 320 # Configure SCIM with Azure AD {{< admonition type="note" >}} -Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} {{< admonition type="warning" >}} @@ -49,7 +49,7 @@ Refer to the [SAML authentication with Azure AD documentation](../../configure-a Before configuring SCIM with Azure AD, ensure you have: -- Grafana Enterprise or Grafana Cloud Advanced +- Grafana Enterprise or a paid Grafana Cloud account with SCIM provisioning enabled. - Admin access to both Grafana and Azure AD - SCIM feature enabled in Grafana diff --git a/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/configure-scim-with-okta/_index.md b/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/configure-scim-with-okta/_index.md index 1555ae72754..50434391f7e 100644 --- a/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/configure-scim-with-okta/_index.md +++ b/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/configure-scim-with-okta/_index.md @@ -19,7 +19,7 @@ weight: 320 # Configure SCIM with Okta {{< admonition type="note" >}} -Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} {{< admonition type="warning" >}} @@ -39,7 +39,7 @@ For more information, refer to the [feature toggles documentation](/docs/grafana Before configuring SCIM with Okta, ensure you have: -- Grafana Enterprise or Grafana Cloud Advanced +- Grafana Enterprise or a paid Grafana Cloud account with SCIM provisioning enabled. - Admin access to both Grafana and Okta - [SAML authentication configured with Okta](../../configure-authentication/saml/configure-saml-with-okta/) - SCIM feature enabled in Grafana diff --git a/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/manage-users-teams/_index.md b/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/manage-users-teams/_index.md index 2b3b0524884..ed9592e5929 100644 --- a/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/manage-users-teams/_index.md +++ b/docs/sources/setup-grafana/configure-security/configure-scim-provisioning/manage-users-teams/_index.md @@ -18,7 +18,7 @@ weight: 310 # Manage users and teams with SCIM {{< admonition type="note" >}} -Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). +Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} {{< admonition type="warning" >}} diff --git a/docs/sources/setup-grafana/configure-security/export-logs.md b/docs/sources/setup-grafana/configure-security/export-logs.md index 7bb7b453fc7..f55336f8a67 100644 --- a/docs/sources/setup-grafana/configure-security/export-logs.md +++ b/docs/sources/setup-grafana/configure-security/export-logs.md @@ -18,7 +18,7 @@ weight: 900 # Export logs of usage insights {{< admonition type="note" >}} -Available in [Grafana Enterprise](../../../introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). +Available in [Grafana Enterprise](../../../introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} By exporting usage logs to Loki, you can directly query them and create dashboards of the information that matters to you most, such as dashboard errors, most active organizations, or your top-10 most-used queries. This configuration is done for you in Grafana Cloud, with provisioned dashboards. Read about them in the [Grafana Cloud documentation](/docs/grafana-cloud/usage-insights/). diff --git a/docs/sources/setup-grafana/configure-security/planning-iam-strategy/index.md b/docs/sources/setup-grafana/configure-security/planning-iam-strategy/index.md index 34d3ccd8dcb..e4a44e54ed6 100644 --- a/docs/sources/setup-grafana/configure-security/planning-iam-strategy/index.md +++ b/docs/sources/setup-grafana/configure-security/planning-iam-strategy/index.md @@ -182,7 +182,7 @@ When connecting Grafana to an identity provider, it's important to think beyond Team sync is a feature that allows you to synchronize teams or groups from your authentication provider with teams in Grafana. This means that users of specific teams or groups in LDAP, OAuth, or SAML will be automatically added or removed as members of corresponding teams in Grafana. Whenever a user logs in, Grafana will check for any changes in the teams or groups of the authentication provider and update the user's teams in Grafana accordingly. This makes it easy to manage user permissions across multiple systems. {{< admonition type="note" >}} -Available in [Grafana Enterprise](../../../introduction/grafana-enterprise/) and [Grafana Cloud Advanced](/docs/grafana-cloud/). +Available in [Grafana Enterprise](../../../introduction/grafana-enterprise/) and to customers on select Grafana Cloud plans. For pricing information, visit [pricing](https://grafana.com/pricing/) or contact our sales team. {{< /admonition >}} {{< admonition type="note" >}} diff --git a/e2e-playwright/dashboard-new-layouts/dashboard-group-panels.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboard-group-panels.spec.ts index 12023c9de6c..9824d0d08df 100644 --- a/e2e-playwright/dashboard-new-layouts/dashboard-group-panels.spec.ts +++ b/e2e-playwright/dashboard-new-layouts/dashboard-group-panels.spec.ts @@ -14,7 +14,7 @@ test.use({ // these tests require a larger viewport test.use({ - viewport: { width: 1280, height: 1080 }, + viewport: { width: 1920, height: 1080 }, }); test.describe( @@ -124,37 +124,49 @@ test.describe( await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow).click(); await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addPanel).last().click(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 2')) - ).toBeVisible(); + const firstRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New row')); + await expect(firstRow).toBeVisible(); + await firstRow.scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) - ).toHaveCount(5); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: firstRow }) + ).toHaveCount(3); + + const secondRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New row 1')); + await expect(secondRow).toBeVisible(); + + await secondRow.scrollIntoViewIfNeeded(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: secondRow }) + ).toHaveCount(1); + + const thirdRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New row 2')); + await expect(thirdRow).toBeVisible(); + + await thirdRow.scrollIntoViewIfNeeded(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: thirdRow }) + ).toHaveCount(1); // Save dashboard and reload await saveDashboard(dashboardPage, selectors); await page.reload(); + await expect(firstRow).toBeVisible(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 2')) - ).toBeVisible(); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: firstRow }) + ).toHaveCount(3); + await expect(secondRow).toBeVisible(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) - ).toHaveCount(5); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: secondRow }) + ).toHaveCount(1); + + thirdRow.scrollIntoViewIfNeeded(); + await expect(thirdRow).toBeVisible(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: thirdRow }) + ).toHaveCount(1); await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click(); @@ -172,15 +184,9 @@ test.describe( await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.deleteButton).click(); await dashboardPage.getByGrafanaSelector(selectors.pages.ConfirmModal.delete).click(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')) - ).toBeHidden(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 2')) - ).toBeHidden(); + await expect(firstRow).toBeVisible(); + await expect(secondRow).toBeHidden(); + await expect(thirdRow).toBeHidden(); await expect( dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) ).toHaveCount(3); @@ -188,15 +194,9 @@ test.describe( await saveDashboard(dashboardPage, selectors); await page.reload(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')) - ).toBeHidden(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 2')) - ).toBeHidden(); + await expect(firstRow).toBeVisible(); + await expect(secondRow).toBeHidden(); + await expect(thirdRow).toBeHidden(); await expect( dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) ).toHaveCount(3); @@ -224,15 +224,21 @@ test.describe( .getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow) .scrollIntoViewIfNeeded(); + const firstRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New row')); + await expect(firstRow).toBeVisible(); + + firstRow.scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')) - ).toBeVisible(); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: firstRow }) + ).toHaveCount(3); + + const secondRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New row 1')); + await expect(secondRow).toBeVisible(); + + secondRow.scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) - ).toHaveCount(6); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: secondRow }) + ).toHaveCount(3); await saveDashboard(dashboardPage, selectors); await page.reload(); @@ -243,15 +249,18 @@ test.describe( .last() .scrollIntoViewIfNeeded(); + await expect(firstRow).toBeVisible(); + await expect(secondRow).toBeVisible(); + + firstRow.scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')) - ).toBeVisible(); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: firstRow }) + ).toHaveCount(3); + + secondRow.scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) - ).toHaveCount(6); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: secondRow }) + ).toHaveCount(3); }); test('can duplicate a row', async ({ dashboardPage, selectors, page }) => { @@ -274,15 +283,21 @@ test.describe( .getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow) .scrollIntoViewIfNeeded(); + const firstRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New row')); + await expect(firstRow).toBeVisible(); + + firstRow.scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')) - ).toBeVisible(); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: firstRow }) + ).toHaveCount(3); + + const secondRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New row 1')); + await expect(secondRow).toBeVisible(); + + secondRow.scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) - ).toHaveCount(6); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: secondRow }) + ).toHaveCount(3); await saveDashboard(dashboardPage, selectors); await page.reload(); @@ -293,15 +308,18 @@ test.describe( .last() .scrollIntoViewIfNeeded(); + await expect(firstRow).toBeVisible(); + await expect(secondRow).toBeVisible(); + + firstRow.scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')) - ).toBeVisible(); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: firstRow }) + ).toHaveCount(3); + + secondRow.scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) - ).toHaveCount(6); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: secondRow }) + ).toHaveCount(3); }); test('can collapse rows', async ({ dashboardPage, selectors, page }) => { @@ -324,26 +342,29 @@ test.describe( .getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow) .scrollIntoViewIfNeeded(); + const firstRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New row')); + await expect(firstRow).toBeVisible(); + + firstRow.scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')) - ).toBeVisible(); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: firstRow }) + ).toHaveCount(3); + + const secondRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New row 1')); + await expect(secondRow).toBeVisible(); + + secondRow.scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) - ).toHaveCount(6); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: secondRow }) + ).toHaveCount(3); // Collapse rows by clicking on their titles await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')).click(); await dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')).click(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')) - ).toBeVisible(); + await expect(firstRow).toBeVisible(); + await expect(secondRow).toBeVisible(); + await expect( dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) ).toHaveCount(0); @@ -351,12 +372,9 @@ test.describe( await saveDashboard(dashboardPage, selectors); await page.reload(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row')) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New row 1')) - ).toBeVisible(); + await expect(firstRow).toBeVisible(); + await expect(secondRow).toBeVisible(); + await expect( dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) ).toHaveCount(0); @@ -684,67 +702,75 @@ test.describe( // Select rows layout await page.getByLabel('Rows').click(); + await dashboardPage + .getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New tab 1')) + .scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab')) + dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New tab 1')) ).toBeVisible(); + await dashboardPage + .getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New tab 2')) + .scrollIntoViewIfNeeded(); + + const firstRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New tab')); + const secondRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New tab 1')); + const thirdRow = dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.wrapper('New tab 2')); + + firstRow.scrollIntoViewIfNeeded(); + await expect(firstRow).toBeVisible(); // Wait for panels to load await expect( dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first() ).toBeVisible(); - await dashboardPage - .getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 1')) - .scrollIntoViewIfNeeded(); + await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 1')) - ).toBeVisible(); - await dashboardPage - .getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 2')) - .scrollIntoViewIfNeeded(); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: firstRow }) + ).toHaveCount(3); + + secondRow.scrollIntoViewIfNeeded(); + await expect(secondRow).toBeVisible(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 2')) - ).toBeVisible(); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: secondRow }) + ).toHaveCount(3); + + thirdRow.scrollIntoViewIfNeeded(); + await expect(thirdRow).toBeVisible(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: thirdRow }) + ).toHaveCount(3); // scroll `New row` into view - this is at the bottom of the dashboard body await dashboardPage .getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow) .scrollIntoViewIfNeeded(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) - ).toHaveCount(9); + await expect(dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow)).toBeVisible(); await saveDashboard(dashboardPage, selectors); await page.reload(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab')) - ).toBeVisible(); + await expect(firstRow).toBeVisible(); + // Wait for panels to load await expect( dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first() ).toBeVisible(); - await dashboardPage - .getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 1')) - .scrollIntoViewIfNeeded(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 1')) - ).toBeVisible(); - await dashboardPage - .getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 2')) - .scrollIntoViewIfNeeded(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.DashboardRow.title('New tab 2')) - ).toBeVisible(); - - // scroll last `New panel` into view - this is at the bottom of the dashboard body - await dashboardPage - .getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) - .last() - .scrollIntoViewIfNeeded(); await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) - ).toHaveCount(9); + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: firstRow }) + ).toHaveCount(3); + + secondRow.scrollIntoViewIfNeeded(); + await expect(secondRow).toBeVisible(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: secondRow }) + ).toHaveCount(3); + + thirdRow.scrollIntoViewIfNeeded(); + await expect(thirdRow).toBeVisible(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel'), { root: thirdRow }) + ).toHaveCount(3); }); test('can group and ungroup new panels into tab with row', async ({ dashboardPage, selectors, page }) => { diff --git a/e2e-playwright/dashboard-new-layouts/dashboards-edit-query-variables.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboards-edit-query-variables.spec.ts index 6c55988a309..2e6b80b7b3c 100644 --- a/e2e-playwright/dashboard-new-layouts/dashboards-edit-query-variables.spec.ts +++ b/e2e-playwright/dashboard-new-layouts/dashboards-edit-query-variables.spec.ts @@ -10,6 +10,10 @@ test.use({ }, }); +test.use({ + viewport: { width: 1920, height: 1080 }, +}); + const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output'; const DASHBOARD_NAME = 'Test variable output'; @@ -55,7 +59,7 @@ test.describe( const firstPreviewOption = dashboardPage .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption) .first(); - await expect(firstPreviewOption).toBeVisible(); + await expect(firstPreviewOption).toBeVisible({ timeout: 15_000 }); const previewOptionText = await firstPreviewOption.textContent(); const previewOption = previewOptionText?.trim() || ''; diff --git a/e2e-playwright/dashboard-new-layouts/dashboards-panel-layouts.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboards-panel-layouts.spec.ts index 78889efbbbe..6cd56e19a11 100644 --- a/e2e-playwright/dashboard-new-layouts/dashboards-panel-layouts.spec.ts +++ b/e2e-playwright/dashboard-new-layouts/dashboards-panel-layouts.spec.ts @@ -12,6 +12,10 @@ test.use({ }, }); +test.use({ + viewport: { width: 1920, height: 1080 }, +}); + test.describe( 'Dashboard Panel Layouts', { @@ -171,6 +175,10 @@ test.describe( test('can change max columns in auto grid layout', async ({ dashboardPage, selectors, page }) => { await importTestDashboard(page, selectors, 'Set max columns'); + await await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first() + ).toBeVisible(); + await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click(); await expect( @@ -384,6 +392,11 @@ async function importTestDashboard(page: Page, selectors: E2ESelectorGroups, tit await page.getByTestId(selectors.components.DataSourcePicker.inputV2).click(); await page.locator('div[data-testid="data-source-card"]').first().click(); await page.getByTestId(selectors.components.ImportDashboardForm.submit).click(); + const undockMenuButton = page.locator('[aria-label="Undock menu"]'); + const undockMenuVisible = await undockMenuButton.isVisible(); + if (undockMenuVisible) { + undockMenuButton.click(); + } } async function saveDashboard(dashboardPage: DashboardPage, selectors: E2ESelectorGroups) { diff --git a/e2e-playwright/dashboard-new-layouts/dashboards-remove-panel.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboards-remove-panel.spec.ts index 5b5202f1455..61fb78ab5a7 100644 --- a/e2e-playwright/dashboard-new-layouts/dashboards-remove-panel.spec.ts +++ b/e2e-playwright/dashboard-new-layouts/dashboards-remove-panel.spec.ts @@ -10,6 +10,10 @@ test.use({ const PAGE_UNDER_TEST = 'edediimbjhdz4b/a-tall-dashboard'; +test.use({ + viewport: { width: 1920, height: 1080 }, +}); + test.describe( 'Dashboard panels', { diff --git a/e2e-playwright/dashboards-suite/dashboard-browse-nested.spec.ts b/e2e-playwright/dashboards-suite/dashboard-browse-nested.spec.ts index 7dfbf455176..48a3c00359f 100644 --- a/e2e-playwright/dashboards-suite/dashboard-browse-nested.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-browse-nested.spec.ts @@ -9,13 +9,13 @@ const NUM_NESTED_DASHBOARDS = 60; test.use({ featureToggles: { - tableNextGen: true, + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', }, }); // TODO change this test so it doesn't conflict with the existing dashboard browse test // probably needs a separate user -test.describe.skip( +test.describe.fixme( 'Dashboard browse (nested)', { tag: ['@dashboards'], @@ -107,7 +107,7 @@ test.describe.skip( await expect(page.getByText('Nested folder 00')).toBeVisible(); // Get the table body container for scrolling - const tableBody = page.getByRole('grid'); + const tableBody = page.getByTestId(selectors.pages.BrowseDashboards.table.body).locator('> div'); // Scroll the page and check visibility of next set of items await tableBody.evaluate((el) => el.scrollTo(0, 2100)); diff --git a/e2e-playwright/dashboards-suite/dashboard-browse.spec.ts b/e2e-playwright/dashboards-suite/dashboard-browse.spec.ts index 98106773c2d..af0d65adc29 100644 --- a/e2e-playwright/dashboards-suite/dashboard-browse.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-browse.spec.ts @@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e'; import testDashboard from '../dashboards/TestDashboard.json'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Dashboard browse', { diff --git a/e2e-playwright/dashboards-suite/dashboard-export-image.spec.ts b/e2e-playwright/dashboards-suite/dashboard-export-image.spec.ts index 5a72d94f9d6..855b1a47c92 100644 --- a/e2e-playwright/dashboards-suite/dashboard-export-image.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-export-image.spec.ts @@ -7,6 +7,7 @@ test.use({ scenes: true, newDashboardSharingComponent: true, sharingDashboardImage: true, // Enable the export image feature + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', }, }); diff --git a/e2e-playwright/dashboards-suite/dashboard-export-json.spec.ts b/e2e-playwright/dashboards-suite/dashboard-export-json.spec.ts index d5a3f4ecac3..18145a6c739 100644 --- a/e2e-playwright/dashboards-suite/dashboard-export-json.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-export-json.spec.ts @@ -1,5 +1,11 @@ import { test, expect } from '@grafana/plugin-e2e'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Export as JSON', { diff --git a/e2e-playwright/dashboards-suite/dashboard-keybindings.spec.ts b/e2e-playwright/dashboards-suite/dashboard-keybindings.spec.ts index 893afe13f09..dd420093696 100644 --- a/e2e-playwright/dashboards-suite/dashboard-keybindings.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-keybindings.spec.ts @@ -1,5 +1,11 @@ import { test, expect } from '@grafana/plugin-e2e'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Dashboard keybindings', { diff --git a/e2e-playwright/dashboards-suite/dashboard-links-without-slug.spec.ts b/e2e-playwright/dashboards-suite/dashboard-links-without-slug.spec.ts index 89a03671f5a..530c74c2485 100644 --- a/e2e-playwright/dashboards-suite/dashboard-links-without-slug.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-links-without-slug.spec.ts @@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e'; import testDashboard from '../dashboards/DataLinkWithoutSlugTest.json'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Dashboard with data links that have no slug', { diff --git a/e2e-playwright/dashboards-suite/dashboard-live-streaming.spec.ts b/e2e-playwright/dashboards-suite/dashboard-live-streaming.spec.ts index dcebf98884d..c8917dfae47 100644 --- a/e2e-playwright/dashboards-suite/dashboard-live-streaming.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-live-streaming.spec.ts @@ -5,6 +5,7 @@ import testDashboard from '../dashboards/DashboardLiveTest.json'; test.use({ featureToggles: { tableNextGen: true, + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', }, }); diff --git a/e2e-playwright/dashboards-suite/dashboard-public-create.spec.ts b/e2e-playwright/dashboards-suite/dashboard-public-create.spec.ts index 3263b4f3011..874209513bc 100644 --- a/e2e-playwright/dashboards-suite/dashboard-public-create.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-public-create.spec.ts @@ -3,6 +3,7 @@ import { test, expect } from '@grafana/plugin-e2e'; test.use({ featureToggles: { newDashboardSharingComponent: false, + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', }, }); diff --git a/e2e-playwright/dashboards-suite/dashboard-public-templating.spec.ts b/e2e-playwright/dashboards-suite/dashboard-public-templating.spec.ts index 60b928c1301..a9bc269427f 100644 --- a/e2e-playwright/dashboards-suite/dashboard-public-templating.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-public-templating.spec.ts @@ -3,6 +3,7 @@ import { test, expect } from '@grafana/plugin-e2e'; test.use({ featureToggles: { newDashboardSharingComponent: false, + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', }, }); diff --git a/e2e-playwright/dashboards-suite/dashboard-share-externally-create.spec.ts b/e2e-playwright/dashboards-suite/dashboard-share-externally-create.spec.ts index 0d991c100e1..b7a0eb5f35e 100644 --- a/e2e-playwright/dashboards-suite/dashboard-share-externally-create.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-share-externally-create.spec.ts @@ -4,6 +4,7 @@ test.use({ featureToggles: { scenes: true, newDashboardSharingComponent: true, + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', }, }); diff --git a/e2e-playwright/dashboards-suite/dashboard-share-internally.spec.ts b/e2e-playwright/dashboards-suite/dashboard-share-internally.spec.ts index d250bb73464..82c9f4a8c17 100644 --- a/e2e-playwright/dashboards-suite/dashboard-share-internally.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-share-internally.spec.ts @@ -4,6 +4,7 @@ test.use({ featureToggles: { scenes: true, newDashboardSharingComponent: true, + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', }, }); diff --git a/e2e-playwright/dashboards-suite/dashboard-share-snapshot-create.spec.ts b/e2e-playwright/dashboards-suite/dashboard-share-snapshot-create.spec.ts index a3390122e92..b60cee55733 100644 --- a/e2e-playwright/dashboards-suite/dashboard-share-snapshot-create.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-share-snapshot-create.spec.ts @@ -6,6 +6,7 @@ test.use({ featureToggles: { scenes: true, newDashboardSharingComponent: true, + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', }, }); diff --git a/e2e-playwright/dashboards-suite/dashboard-templating.spec.ts b/e2e-playwright/dashboards-suite/dashboard-templating.spec.ts index b31fecf230f..1b3f8a326fa 100644 --- a/e2e-playwright/dashboards-suite/dashboard-templating.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-templating.spec.ts @@ -4,6 +4,9 @@ const DASHBOARD_UID = 'HYaGDGIMk'; test.use({ timezoneId: 'Pacific/Easter', + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, }); test.describe( diff --git a/e2e-playwright/dashboards-suite/dashboard-time-zone.spec.ts b/e2e-playwright/dashboards-suite/dashboard-time-zone.spec.ts new file mode 100644 index 00000000000..2d21b0b6af5 --- /dev/null +++ b/e2e-playwright/dashboards-suite/dashboard-time-zone.spec.ts @@ -0,0 +1,289 @@ +import { addDays, addHours, differenceInCalendarDays, differenceInMinutes, isBefore, parseISO, toDate } from 'date-fns'; +import { Page } from 'playwright-core'; + +import { test, expect, DashboardPage, E2ESelectorGroups } from '@grafana/plugin-e2e'; + +const TIMEZONE_DASHBOARD_UID = 'd41dbaa2-a39e-4536-ab2b-caca52f1a9c8'; + +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + +test.describe( + 'Dashboard time zone support', + { + tag: ['@dashboards'], + }, + () => { + test('Tests dashboard time zone scenarios', async ({ page, gotoDashboardPage, selectors }) => { + const dashboardPage = await gotoDashboardPage({ uid: TIMEZONE_DASHBOARD_UID }); + + const fromTimeZone = 'UTC'; + const toTimeZone = 'America/Chicago'; + const offset = offsetBetweenTimeZones(toTimeZone, fromTimeZone); + + // Enter edit mode + await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click(); + + // Open dashboard settings + await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.settingsButton).click(); + + // Change timezone to UTC + await page.getByTestId(selectors.components.TimeZonePicker.containerV2).click(); + await page.getByRole('option', { name: 'Coordinated Universal Time ' }).click(); + + // Close settings and refresh + await dashboardPage + .getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton) + .click(); + await dashboardPage.getByGrafanaSelector(selectors.components.RefreshPicker.runButtonV2).click(); + + const panelsToCheck = ['Panel in timezone']; + + const timesInUtc: Record = {}; + + // Verify all panels are visible + for (const title of panelsToCheck) { + await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title))).toBeVisible(); + const timeCell = dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title(title)) + .getByRole('row') + .nth(1) + .getByRole('cell') + .first(); + const time = await timeCell.textContent(); + if (time) { + timesInUtc[title] = time; + } + } + + // Open dashboard settings + await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.settingsButton).click(); + + // Change timezone to America/Chicago + await page.getByTestId(selectors.components.TimeZonePicker.containerV2).click(); + await page.getByRole('option', { name: toTimeZone }).click(); + + // Close settings and refresh + await dashboardPage + .getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton) + .click(); + await dashboardPage.getByGrafanaSelector(selectors.components.RefreshPicker.runButtonV2).click(); + + // Verify panels are still visible after timezone change + for (const title of panelsToCheck) { + await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title))).toBeVisible(); + const timeCell = dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title(title)) + .getByRole('row') + .nth(1) + .getByRole('cell') + .first(); + await expect(async () => { + const inUtc = timesInUtc[title]; + const inTz = await timeCell.textContent(); + expect(inTz).not.toBeNull(); + if (inTz) { + const isCorrect = isTimeCorrect(inUtc, inTz, offset); + expect(isCorrect).toEqual(true); + } + }).toPass(); + } + }); + + test('Tests relative timezone support and overrides', async ({ page, gotoDashboardPage, selectors }) => { + // Open dashboard + const dashboardPage = await gotoDashboardPage({ + uid: TIMEZONE_DASHBOARD_UID, + }); + + // Switch to Browser timezone + await setTimeRange(page, dashboardPage, selectors, { + from: 'now-6h', + to: 'now', + zone: 'Browser', + }); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel with relative time override')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + + // Today so far, still in Browser timezone + await setTimeRange(page, dashboardPage, selectors, { + from: 'now/d', + to: 'now', + }); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel with relative time override')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel in timezone')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + + // Test UTC timezone + await setTimeRange(page, dashboardPage, selectors, { + from: 'now-6h', + to: 'now', + zone: 'Coordinated Universal Time', + }); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel with relative time override')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + + // Today so far, still in UTC timezone + await setTimeRange(page, dashboardPage, selectors, { + from: 'now/d', + to: 'now', + }); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel with relative time override')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel in timezone')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + + // Test Tokyo timezone + await setTimeRange(page, dashboardPage, selectors, { + from: 'now-6h', + to: 'now', + zone: 'Asia/Tokyo', + }); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel with relative time override')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + + // Today so far, still in Tokyo timezone + await setTimeRange(page, dashboardPage, selectors, { + from: 'now/d', + to: 'now', + }); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel with relative time override')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel in timezone')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + + // Test LA timezone + await setTimeRange(page, dashboardPage, selectors, { + from: 'now-6h', + to: 'now', + zone: 'America/Los Angeles', + }); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel with relative time override')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + + // Today so far, still in LA timezone + await setTimeRange(page, dashboardPage, selectors, { + from: 'now/d', + to: 'now', + }); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel with relative time override')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + + await expect( + dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Panel in timezone')) + .locator('[role="row"]') + .filter({ hasText: '00:00:00' }) + ).toBeVisible(); + }); + } +); + +// Helper function to set time range with optional timezone +async function setTimeRange( + page: Page, + dashboardPage: DashboardPage, + selectors: E2ESelectorGroups, + options: { from: string; to: string; zone?: string } +) { + await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.openButton).click(); + + await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.fromField).fill(options.from); + await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.toField).fill(options.to); + + if (options.zone) { + await page.getByRole('button', { name: 'Change time settings' }).click(); + await page.getByTestId(selectors.components.TimeZonePicker.containerV2).click(); + await page.getByRole('option', { name: options.zone }).click(); + } + + await dashboardPage.getByGrafanaSelector(selectors.components.TimePicker.applyTimeRange).click(); +} + +const isTimeCorrect = (inUtc: string, inTz: string, offset: number): boolean => { + if (inUtc === inTz) { + // we need to catch issues when timezone isn't changed for some reason like https://github.com/grafana/grafana/issues/35504 + return false; + } + + const utcDate = toDate(parseISO(inUtc)); + const utcDateWithOffset = addHours(toDate(parseISO(inUtc)), offset); + const dayDifference = differenceInCalendarDays(utcDate, utcDateWithOffset); // if the utcDate +/- offset is the day before/after then we need to adjust reference + const dayOffset = isBefore(utcDateWithOffset, utcDate) ? dayDifference * -1 : dayDifference; + const tzDate = addDays(toDate(parseISO(inTz)), dayOffset); // adjust tzDate with any dayOffset + const diff = Math.abs(differenceInMinutes(utcDate, tzDate)); // use Math.abs if tzDate is in future + + return diff <= Math.abs(offset * 60); +}; + +const offsetBetweenTimeZones = (timeZone1: string, timeZone2: string, when: Date = new Date()): number => { + const t1 = convertDateToAnotherTimeZone(when, timeZone1); + const t2 = convertDateToAnotherTimeZone(when, timeZone2); + return (t1.getTime() - t2.getTime()) / (1000 * 60 * 60); +}; + +const convertDateToAnotherTimeZone = (date: Date, timeZone: string): Date => { + const dateString = date.toLocaleString('en-US', { + timeZone: timeZone, + }); + return new Date(dateString); +}; diff --git a/e2e-playwright/dashboards-suite/dashboard-timepicker.spec.ts b/e2e-playwright/dashboards-suite/dashboard-timepicker.spec.ts index e57c56b5afe..f7a5c4b9dec 100644 --- a/e2e-playwright/dashboards-suite/dashboard-timepicker.spec.ts +++ b/e2e-playwright/dashboards-suite/dashboard-timepicker.spec.ts @@ -15,6 +15,9 @@ test.use({ cookies: [], origins: [], }, + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, }); test.describe( diff --git a/e2e-playwright/dashboards-suite/embedded-dashboard.spec.ts b/e2e-playwright/dashboards-suite/embedded-dashboard.spec.ts index b6479d8007c..342b912a48a 100644 --- a/e2e-playwright/dashboards-suite/embedded-dashboard.spec.ts +++ b/e2e-playwright/dashboards-suite/embedded-dashboard.spec.ts @@ -1,5 +1,11 @@ import { test, expect } from '@grafana/plugin-e2e'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Embedded dashboard', { diff --git a/e2e-playwright/dashboards-suite/general-dashboards.spec.ts b/e2e-playwright/dashboards-suite/general-dashboards.spec.ts index 0a1978f3d69..3cdd27ed954 100644 --- a/e2e-playwright/dashboards-suite/general-dashboards.spec.ts +++ b/e2e-playwright/dashboards-suite/general-dashboards.spec.ts @@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e'; const PAGE_UNDER_TEST = 'edediimbjhdz4b/a-tall-dashboard'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Dashboards', { diff --git a/e2e-playwright/dashboards-suite/import-dashboard.spec.ts b/e2e-playwright/dashboards-suite/import-dashboard.spec.ts index cd905ac56b8..7ec014c728b 100644 --- a/e2e-playwright/dashboards-suite/import-dashboard.spec.ts +++ b/e2e-playwright/dashboards-suite/import-dashboard.spec.ts @@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e'; import testDashboard from '../dashboards/TestDashboard.json'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Import Dashboards Test', { diff --git a/e2e-playwright/dashboards-suite/load-options-from-url.spec.ts b/e2e-playwright/dashboards-suite/load-options-from-url.spec.ts index ce1293acc2c..c2f45459431 100644 --- a/e2e-playwright/dashboards-suite/load-options-from-url.spec.ts +++ b/e2e-playwright/dashboards-suite/load-options-from-url.spec.ts @@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e'; const PAGE_UNDER_TEST = '-Y-tnEDWk/templating-nested-template-variables'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Variables - Load options from Url', { diff --git a/e2e-playwright/dashboards-suite/new-constant-variable.spec.ts b/e2e-playwright/dashboards-suite/new-constant-variable.spec.ts index 99fa52b3a5c..3e28a6f49bc 100644 --- a/e2e-playwright/dashboards-suite/new-constant-variable.spec.ts +++ b/e2e-playwright/dashboards-suite/new-constant-variable.spec.ts @@ -3,6 +3,12 @@ import { test, expect } from '@grafana/plugin-e2e'; const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output'; const DASHBOARD_NAME = 'Test variable output'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Variables - Constant', { diff --git a/e2e-playwright/dashboards-suite/new-custom-variable.spec.ts b/e2e-playwright/dashboards-suite/new-custom-variable.spec.ts index 30a16a7dae0..cc3bda551d9 100644 --- a/e2e-playwright/dashboards-suite/new-custom-variable.spec.ts +++ b/e2e-playwright/dashboards-suite/new-custom-variable.spec.ts @@ -50,6 +50,12 @@ async function assertPreviewValues( } } +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Variables - Custom', { diff --git a/e2e-playwright/dashboards-suite/new-datasource-variable.spec.ts b/e2e-playwright/dashboards-suite/new-datasource-variable.spec.ts index a769f8438cb..988d79f78ca 100644 --- a/e2e-playwright/dashboards-suite/new-datasource-variable.spec.ts +++ b/e2e-playwright/dashboards-suite/new-datasource-variable.spec.ts @@ -3,6 +3,12 @@ import { test, expect } from '@grafana/plugin-e2e'; const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output'; const DASHBOARD_NAME = 'Test variable output'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Variables - Datasource', { diff --git a/e2e-playwright/dashboards-suite/new-interval-variable.spec.ts b/e2e-playwright/dashboards-suite/new-interval-variable.spec.ts index dbb670c739e..0310f6c3500 100644 --- a/e2e-playwright/dashboards-suite/new-interval-variable.spec.ts +++ b/e2e-playwright/dashboards-suite/new-interval-variable.spec.ts @@ -16,6 +16,12 @@ async function assertPreviewValues( } } +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Variables - Interval', { diff --git a/e2e-playwright/dashboards-suite/new-query-variable.spec.ts b/e2e-playwright/dashboards-suite/new-query-variable.spec.ts index d289142a097..212f0ecd018 100644 --- a/e2e-playwright/dashboards-suite/new-query-variable.spec.ts +++ b/e2e-playwright/dashboards-suite/new-query-variable.spec.ts @@ -3,6 +3,12 @@ import { test, expect } from '@grafana/plugin-e2e'; const PAGE_UNDER_TEST = '-Y-tnEDWk/templating-nested-template-variables'; const DASHBOARD_NAME = 'Templating - Nested Template Variables'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Variables - Query - Add variable', { diff --git a/e2e-playwright/dashboards-suite/new-text-box-variable.spec.ts b/e2e-playwright/dashboards-suite/new-text-box-variable.spec.ts index 4f5a856c5de..5c49254284f 100644 --- a/e2e-playwright/dashboards-suite/new-text-box-variable.spec.ts +++ b/e2e-playwright/dashboards-suite/new-text-box-variable.spec.ts @@ -3,6 +3,12 @@ import { test, expect } from '@grafana/plugin-e2e'; const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output'; const DASHBOARD_NAME = 'Test variable output'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Variables - Text box', { diff --git a/e2e-playwright/dashboards-suite/repeating-a-panel-horizontally.spec.ts b/e2e-playwright/dashboards-suite/repeating-a-panel-horizontally.spec.ts index 22aacc41c88..920bc343275 100644 --- a/e2e-playwright/dashboards-suite/repeating-a-panel-horizontally.spec.ts +++ b/e2e-playwright/dashboards-suite/repeating-a-panel-horizontally.spec.ts @@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e'; const PAGE_UNDER_TEST = 'WVpf2jp7z/repeating-a-panel-horizontally'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Repeating a panel horizontally', { diff --git a/e2e-playwright/dashboards-suite/repeating-a-panel-vertically.spec.ts b/e2e-playwright/dashboards-suite/repeating-a-panel-vertically.spec.ts index 0e6755c05e5..3dc9cfcbcc1 100644 --- a/e2e-playwright/dashboards-suite/repeating-a-panel-vertically.spec.ts +++ b/e2e-playwright/dashboards-suite/repeating-a-panel-vertically.spec.ts @@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e'; const PAGE_UNDER_TEST = 'OY8Ghjt7k/repeating-a-panel-vertically'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Repeating a panel vertically', { diff --git a/e2e-playwright/dashboards-suite/repeating-an-empty-row.spec.ts b/e2e-playwright/dashboards-suite/repeating-an-empty-row.spec.ts index c326f55552d..31a60079444 100644 --- a/e2e-playwright/dashboards-suite/repeating-an-empty-row.spec.ts +++ b/e2e-playwright/dashboards-suite/repeating-an-empty-row.spec.ts @@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e'; const PAGE_UNDER_TEST = 'dtpl2Ctnk/repeating-an-empty-row'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Repeating empty rows', { diff --git a/e2e-playwright/dashboards-suite/set-options-from-ui.spec.ts b/e2e-playwright/dashboards-suite/set-options-from-ui.spec.ts index ccf6056d695..556ec524713 100644 --- a/e2e-playwright/dashboards-suite/set-options-from-ui.spec.ts +++ b/e2e-playwright/dashboards-suite/set-options-from-ui.spec.ts @@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e'; const PAGE_UNDER_TEST = '-Y-tnEDWk/templating-nested-template-variables'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Variables - Set options from ui', { diff --git a/e2e-playwright/dashboards-suite/snapshot-create.spec.ts b/e2e-playwright/dashboards-suite/snapshot-create.spec.ts index a2ae416d84b..0a37d134b2b 100644 --- a/e2e-playwright/dashboards-suite/snapshot-create.spec.ts +++ b/e2e-playwright/dashboards-suite/snapshot-create.spec.ts @@ -5,6 +5,7 @@ const DASHBOARD_UID = 'ZqZnVvFZz'; test.use({ featureToggles: { newDashboardSharingComponent: false, // Use legacy sharing component for this test + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', }, }); diff --git a/e2e-playwright/dashboards-suite/templating-dashboard-links-and-variables.spec.ts b/e2e-playwright/dashboards-suite/templating-dashboard-links-and-variables.spec.ts index 54bfdb771ba..813815f7f39 100644 --- a/e2e-playwright/dashboards-suite/templating-dashboard-links-and-variables.spec.ts +++ b/e2e-playwright/dashboards-suite/templating-dashboard-links-and-variables.spec.ts @@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e'; const DASHBOARD_UID = 'yBCC3aKGk'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'Templating dashboard links and variables', { diff --git a/e2e-playwright/dashboards-suite/textbox-variables.spec.ts b/e2e-playwright/dashboards-suite/textbox-variables.spec.ts index 20815446223..c992456a1bc 100644 --- a/e2e-playwright/dashboards-suite/textbox-variables.spec.ts +++ b/e2e-playwright/dashboards-suite/textbox-variables.spec.ts @@ -4,6 +4,12 @@ import { test, expect, E2ESelectorGroups, DashboardPage } from '@grafana/plugin- const PAGE_UNDER_TEST = 'AejrN1AMz'; +test.use({ + featureToggles: { + kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', + }, +}); + test.describe( 'TextBox - load options scenarios', { diff --git a/e2e-playwright/dashboards/TestV2Dashboard.json b/e2e-playwright/dashboards/TestV2Dashboard.json index 25917da59fa..e4b81d82bf4 100644 --- a/e2e-playwright/dashboards/TestV2Dashboard.json +++ b/e2e-playwright/dashboards/TestV2Dashboard.json @@ -2,32 +2,27 @@ "apiVersion": "dashboard.grafana.app/v2beta1", "kind": "Dashboard", "metadata": { - "name": "fa400625-2a44-4add-a369-e6c972eb4bd6", - "generation": 1, - "creationTimestamp": "2025-05-27T11:40:22Z", - "labels": {}, - "annotations": {} + "name": "addfpww", + "namespace": "default", + "uid": "AvXN09JdxuVNDqODs5IYU2iStlk5ntizuPGIfY1ywZgX", + "resourceVersion": "1", + "generation": 2, + "creationTimestamp": "2025-07-31T13:37:11Z", + "labels": { + "grafana.app/deprecatedInternalID": "6844" + }, + "annotations": { + "grafana.app/createdBy": "user:cejvsh18uudxcf", + "grafana.app/updatedBy": "user:cejvsh18uudxcf", + "grafana.app/updatedTimestamp": "2025-07-31T13:37:11Z", + "grafana.app/folder": "", + "grafana.app/saved-from-ui": "Grafana v12.2.0-pre (69d1226c9a)" + } }, "spec": { - "annotations": [ - { - "kind": "AnnotationQuery", - "spec": { - "builtIn": true, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "query": { - "group": "grafana", - "kind": "DataQuery", - "spec": {}, - "version": "v0" - } - } - } - ], + "annotations": [], "cursorSync": "Off", + "description": "", "editable": true, "elements": { "panel-1": { @@ -42,6 +37,9 @@ "spec": { "hidden": false, "query": { + "datasource": { + "name": "PD8C576611E62080A" + }, "group": "grafana-testdata-datasource", "kind": "DataQuery", "spec": {}, @@ -60,7 +58,8 @@ "links": [], "title": "New panel", "vizConfig": { - "kind": "timeseries", + "group": "timeseries", + "kind": "VizConfig", "spec": { "fieldConfig": { "defaults": { @@ -128,9 +127,9 @@ "mode": "single", "sort": "none" } - }, - "pluginVersion": "12.1.0-pre" - } + } + }, + "version": "12.2.0-pre" } } }, @@ -146,9 +145,15 @@ "spec": { "hidden": false, "query": { + "datasource": { + "name": "PD8C576611E62080A" + }, "group": "grafana-testdata-datasource", "kind": "DataQuery", - "spec": {}, + "spec": { + "scenarioId": "random_walk", + "seriesCount": 1 + }, "version": "v0" }, "refId": "A" @@ -164,7 +169,8 @@ "links": [], "title": "New panel", "vizConfig": { - "kind": "timeseries", + "group": "timeseries", + "kind": "VizConfig", "spec": { "fieldConfig": { "defaults": { @@ -232,9 +238,9 @@ "mode": "single", "sort": "none" } - }, - "pluginVersion": "12.1.0-pre" - } + } + }, + "version": "12.2.0-pre" } } }, @@ -250,6 +256,9 @@ "spec": { "hidden": false, "query": { + "datasource": { + "name": "PD8C576611E62080A" + }, "group": "grafana-testdata-datasource", "kind": "DataQuery", "spec": {}, @@ -268,7 +277,8 @@ "links": [], "title": "New panel", "vizConfig": { - "kind": "timeseries", + "group": "timeseries", + "kind": "VizConfig", "spec": { "fieldConfig": { "defaults": { @@ -336,9 +346,9 @@ "mode": "single", "sort": "none" } - }, - "pluginVersion": "12.1.0-pre" - } + } + }, + "version": "12.2.0-pre" } } } @@ -402,7 +412,7 @@ "timezone": "browser", "to": "now" }, - "title": "Test V2 Dashboard", + "title": "TEST!!!!", "variables": [] }, "status": {} diff --git a/e2e-playwright/panels-suite/table-kitchenSink.spec.ts b/e2e-playwright/panels-suite/table-kitchenSink.spec.ts index f386fb4175c..8ac43ceebb1 100644 --- a/e2e-playwright/panels-suite/table-kitchenSink.spec.ts +++ b/e2e-playwright/panels-suite/table-kitchenSink.spec.ts @@ -4,12 +4,7 @@ import { test, expect } from '@grafana/plugin-e2e'; const DASHBOARD_UID = 'dcb9f5e9-8066-4397-889e-864b99555dbb'; -test.use({ - viewport: { width: 2000, height: 1080 }, - featureToggles: { - tableNextGen: true, - }, -}); +test.use({ viewport: { width: 2000, height: 1080 }, featureToggles: { tableNextGen: true } }); // helper utils const waitForTableLoad = async (loc: Page | Locator) => { @@ -46,337 +41,324 @@ const getColumnIdx = async (loc: Page | Locator, columnName: string) => { return result; }; -test.describe( - 'Panels test: Table - Kitchen Sink', - { - tag: ['@panels'], - }, - () => { - test('Tests word wrap, hover overflow, and cell inspect', async ({ gotoDashboardPage, selectors, page }) => { - const dashboardPage = await gotoDashboardPage({ - uid: DASHBOARD_UID, - queryParams: new URLSearchParams({ editPanel: '1' }), - }); +test.describe('Panels test: Table - Kitchen Sink', { tag: ['@panels', '@table'] }, () => { + test('Tests word wrap, hover overflow, and cell inspect', async ({ gotoDashboardPage, selectors, page }) => { + const dashboardPage = await gotoDashboardPage({ + uid: DASHBOARD_UID, + queryParams: new URLSearchParams({ editPanel: '1' }), + }); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) - ).toBeVisible(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) + ).toBeVisible(); - // to avoid a race condition when counting up , wait for react-data-grid to finish rendering. - await waitForTableLoad(page); + // to avoid a race condition when counting up , wait for react-data-grid to finish rendering. + await waitForTableLoad(page); - const longTextColIdx = await getColumnIdx(page, 'Long Text'); + const longTextColIdx = await getColumnIdx(page, 'Long Text'); - // text wrapping is enabled by default on this panel. - await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeGreaterThan(100); + // text wrapping is enabled by default on this panel. + await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeGreaterThan(100); - // toggle the lorem ipsum column's wrap text toggle and confirm that the height shrinks. - await dashboardPage - .getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Wrap text')) - .last() - .click(); - await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeLessThan(100); + // FIXME very bad selector to get the correct "wrap text" toggle here. + // toggle the lorem ipsum column's wrap text toggle and confirm that the height shrinks. + await page + .locator('[id="Override 13"]') + .locator(`[aria-label="${selectors.components.PanelEditor.OptionsPane.fieldLabel('Wrap text')}"]`) + .click(); + await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeLessThan(100); - // test that hover overflow works. - const loremIpsumCell = await getCell(page, 1, longTextColIdx); - await loremIpsumCell.scrollIntoViewIfNeeded(); - await loremIpsumCell.hover(); - await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeGreaterThan(100); - await (await getCell(page, 1, longTextColIdx + 1)).hover(); - await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeLessThan(100); + // test that hover overflow works. + const loremIpsumCell = await getCell(page, 1, longTextColIdx); + await loremIpsumCell.scrollIntoViewIfNeeded(); + await loremIpsumCell.hover(); + await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeGreaterThan(100); + await (await getCell(page, 1, longTextColIdx + 1)).hover(); + await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeLessThan(100); - // enable cell inspect, confirm that hover no longer triggers. + // enable cell inspect, confirm that hover no longer triggers. + await dashboardPage + .getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Cell options Cell value inspect')) + .first() + .locator('label[for="custom.inspect"]') + .click(); + await loremIpsumCell.hover(); + await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeLessThan(100); + + // click cell inspect, check that cell inspection pops open in the side as we'd expect. + await loremIpsumCell.getByLabel('Inspect value').click(); + const loremIpsumText = await loremIpsumCell.textContent(); + expect(loremIpsumText).toBeDefined(); + await expect(page.getByRole('dialog').getByText(loremIpsumText!)).toBeVisible(); + }); + + test('Tests visibility and display name via overrides', async ({ gotoDashboardPage, selectors, page }) => { + const dashboardPage = await gotoDashboardPage({ + uid: DASHBOARD_UID, + queryParams: new URLSearchParams({ editPanel: '1' }), + }); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) + ).toBeVisible(); + + // confirm that "State" column is hidden by default. + expect(page.getByRole('row').nth(0)).not.toContainText('State'); + + // toggle the "State" column visibility and test that it appears before re-hiding it. + // FIXME this selector is utterly godawful, but there's no way to give testIds or aria-labels or anything to + // the panel editor builder. we should fix that to make e2e's easier to write for our team. + const hideStateColumnSwitch = page.locator('[id="Override 12"]').locator('label').last(); + await hideStateColumnSwitch.click(); + expect(page.getByRole('row').nth(0)).toContainText('State'); + + // now change the display name of the "State" column. + // FIXME it would be good to have a better selector here too. + const displayNameInput = page.locator('[id="Override 12"]').locator('input[value="State"]').last(); + await displayNameInput.fill('State (renamed)'); + await displayNameInput.press('Enter'); + expect(page.getByRole('row').nth(0)).toContainText('State (renamed)'); + }); + + // we test niche cases for sorting, filtering, pagination, etc. in a unit tests already. + // we mainly want to test the happiest paths for these in e2es as well to check for integration + // issues, but the unit tests can confirm that the internal logic works as expected much more quickly and thoroughly. + // hashtag testing pyramid. + test('Tests sorting by column', async ({ gotoDashboardPage, selectors, page }) => { + const dashboardPage = await gotoDashboardPage({ + uid: DASHBOARD_UID, + queryParams: new URLSearchParams({ editPanel: '1' }), + }); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) + ).toBeVisible(); + + // click the "State" column header to sort it. + const stateColumnHeader = await getCell(page, 0, 1); + + await stateColumnHeader.getByText('Info').click(); + await expect(stateColumnHeader).toHaveAttribute('aria-sort', 'ascending'); + expect(getCell(page, 1, 1)).resolves.toContainText('down'); // down or down fast + + await stateColumnHeader.getByText('Info').click(); + await expect(stateColumnHeader).toHaveAttribute('aria-sort', 'descending'); + expect(getCell(page, 1, 1)).resolves.toContainText('up'); // up or up fast + + await stateColumnHeader.getByText('Info').click(); + await expect(stateColumnHeader).not.toHaveAttribute('aria-sort'); + }); + + test('Tests filtering within a column', async ({ gotoDashboardPage, selectors, page }) => { + const dashboardPage = await gotoDashboardPage({ + uid: DASHBOARD_UID, + queryParams: new URLSearchParams({ editPanel: '1' }), + }); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) + ).toBeVisible(); + + await waitForTableLoad(page); + + const infoColumnIdx = await getColumnIdx(page, 'Info'); + + const stateColumnHeader = page.getByRole('columnheader').nth(infoColumnIdx); + + // get the first value in the "State" column, filter it out, then check that it went away. + const firstStateValue = (await (await getCell(page, 1, infoColumnIdx)).textContent())!; + await stateColumnHeader.getByTestId(selectors.components.Panels.Visualization.TableNG.Filters.HeaderButton).click(); + const filterContainer = dashboardPage.getByGrafanaSelector( + selectors.components.Panels.Visualization.TableNG.Filters.Container + ); + + await expect(filterContainer).toBeVisible(); + + // select all, then click the first value to unselect it, filtering it out. + await filterContainer.getByTestId(selectors.components.Panels.Visualization.TableNG.Filters.SelectAll).click(); + await filterContainer.getByTitle(firstStateValue, { exact: true }).locator('label').click(); + await filterContainer.getByRole('button', { name: 'Ok' }).click(); + + // make sure the filter container closed when we clicked "Ok". + await expect(filterContainer).not.toBeVisible(); + + // did it actually filter out our value? + await expect(getCell(page, 1, infoColumnIdx)).resolves.not.toHaveText(firstStateValue); + }); + + test('Tests pagination, row height adjustment', async ({ gotoDashboardPage, selectors, page }) => { + const rowRe = /([\d]+) - ([\d]+) of ([\d]+) rows/; + const getRowStatus = async (page: Page | Locator) => { + const text = (await page.getByText(rowRe).textContent()) ?? ''; + const match = text.match(rowRe); + return { + start: parseInt(match?.[1] ?? '0', 10), + end: parseInt(match?.[2] ?? '0', 10), + total: parseInt(match?.[3] ?? '0', 10), + }; + }; + + const dashboardPage = await gotoDashboardPage({ + uid: DASHBOARD_UID, + queryParams: new URLSearchParams({ editPanel: '1' }), + }); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) + ).toBeVisible(); + + await page + .getByLabel(selectors.components.PanelEditor.OptionsPane.fieldLabel(`Enable pagination`), { exact: true }) + .click(); + + // because of text wrapping, we're guaranteed to only be showing a single row when we enable pagination. + await expect(page.getByText(/([\d]+) - ([\d]+) of ([\d]+) rows/)).toBeVisible(); + + // FIXME horrible selector for the "Wrap text" toggle for the "Long text" column. + await page + .locator('[id="Override 13"]') + .locator(`[aria-label="${selectors.components.PanelEditor.OptionsPane.fieldLabel('Wrap text')}"]`) + .click(); + + // any number of rows that is not "1" is allowed here, we don't want to police the exact number of rows that + // are rendered since there are tons of factors which could effect this. we do want to grab this number for comparison + // in a second, though. + const smallRowStatus = await getRowStatus(page); + expect(smallRowStatus.end).toBeGreaterThan(1); + expect(page.getByRole('grid').getByRole('row')).toHaveCount(smallRowStatus.end + 2); // +2 for header and footer rows + + // change cell height to Large + await dashboardPage + .getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Table Cell height')) + .locator('input') + .last() + .click(); + const largeRowStatus = await getRowStatus(page); + expect(largeRowStatus.end).toBeLessThan(smallRowStatus.end); + expect(page.getByRole('grid').getByRole('row')).toHaveCount(largeRowStatus.end + 2); // +2 for header and footer rows + + // click a page over with the directional nav + await page.getByLabel('next page').click(); + const nextPageStatus = await getRowStatus(page); + expect(nextPageStatus.start).toBe(largeRowStatus.end + 1); + expect(nextPageStatus.end).toBe(largeRowStatus.end * 2); + expect(nextPageStatus.total).toBe(largeRowStatus.total); + + // click a page number + await page.getByTestId('data-testid panel content').getByRole('navigation').getByText('4', { exact: true }).click(); + const fourthPageStatus = await getRowStatus(page); + expect(fourthPageStatus.start).toBe(largeRowStatus.end * 3 + 1); + expect(fourthPageStatus.end).toBe(largeRowStatus.end * 4); + expect(fourthPageStatus.total).toBe(largeRowStatus.total); + }); + + test.skip('Tests DataLinks (single and multi) and actions', async ({ gotoDashboardPage, selectors, page }) => { + const addDataLink = async (title: string, url: string) => { await dashboardPage .getByGrafanaSelector( - selectors.components.PanelEditor.OptionsPane.fieldLabel('Cell options Cell value inspect') + selectors.components.PanelEditor.OptionsPane.fieldLabel('Data links and actions Data links') ) - .first() - .locator('label[for="custom.inspect"]') + .locator('button') + .filter({ hasText: 'Add link' }) .click(); - await loremIpsumCell.hover(); - await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeLessThan(100); - // click cell inspect, check that cell inspection pops open in the side as we'd expect. - await loremIpsumCell.getByLabel('Inspect value').click(); - const loremIpsumText = await loremIpsumCell.textContent(); - expect(loremIpsumText).toBeDefined(); - await expect(page.getByRole('dialog').getByText(loremIpsumText!)).toBeVisible(); + // DataLinks dialog has popped open - fill it in and add a global datalink. + await expect(page.getByRole('dialog')).toBeVisible(); + await page.getByRole('dialog').locator('#link-title').fill(title); + await page.getByRole('dialog').locator('#data-link-input [contenteditable="true"]').focus(); + await page.getByRole('dialog').locator('#data-link-input [contenteditable="true"]').fill(url); + await page.getByRole('dialog').locator('#data-link-input [contenteditable="true"]').blur(); + await page.getByRole('dialog').locator('button[aria-disabled="false"]').filter({ hasText: 'Save' }).click(); + await expect(page.getByRole('dialog')).not.toBeVisible(); + }; + + const dashboardPage = await gotoDashboardPage({ + uid: DASHBOARD_UID, + queryParams: new URLSearchParams({ editPanel: '1' }), }); - test('Tests visibility and display name via overrides', async ({ gotoDashboardPage, selectors, page }) => { - const dashboardPage = await gotoDashboardPage({ - uid: DASHBOARD_UID, - queryParams: new URLSearchParams({ editPanel: '1' }), - }); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) + ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) - ).toBeVisible(); + // disable text wrapping for this test to make it easier to click the links, the long lorem ipsum + // can push the links off the screen. + // FIXME very bad selector to get the correct "wrap text" toggle here. + await page + .locator('[id="Override 13"]') + .locator(`[aria-label="${selectors.components.PanelEditor.OptionsPane.fieldLabel('Wrap text')}"]`) + .click(); - // confirm that "State" column is hidden by default. - expect(page.getByRole('row').nth(0)).not.toContainText('State'); + const infoColumnIdx = await getColumnIdx(page, 'Info'); + const pillColIdx = await getColumnIdx(page, 'Pills'); + const dataLinkColIdx = await getColumnIdx(page, 'Data Link'); - // toggle the "State" column visibility and test that it appears before re-hiding it. - // FIXME this selector is utterly godawful, but there's no way to give testIds or aria-labels or anything to - // the panel editor builder. we should fix that to make e2e's easier to write for our team. - const hideStateColumnSwitch = page.locator('[id="Override 12"]').locator('label').last(); - await hideStateColumnSwitch.click(); - expect(page.getByRole('row').nth(0)).toContainText('State'); + // Info column has a single DataLink by default. + const infoCell = await getCell(page, 1, infoColumnIdx); + await expect(infoCell.locator('a')).toBeVisible(); + expect(infoCell.locator('a')).toHaveAttribute('href'); + expect(infoCell.locator('a')).not.toHaveAttribute('aria-haspopup'); - // now change the display name of the "State" column. - // FIXME it would be good to have a better selector here too. - const displayNameInput = page.locator('[id="Override 12"]').locator('input[value="State"]').last(); - await displayNameInput.fill('State (renamed)'); - await displayNameInput.press('Enter'); - expect(page.getByRole('row').nth(0)).toContainText('State (renamed)'); - }); + // now, add a DataLink to the whole table + await addDataLink('Test link', 'https://grafana.com'); - // we test niche cases for sorting, filtering, pagination, etc. in a unit tests already. - // we mainly want to test the happiest paths for these in e2es as well to check for integration - // issues, but the unit tests can confirm that the internal logic works as expected much more quickly and thoroughly. - // hashtag testing pyramid. - test('Tests sorting by column', async ({ gotoDashboardPage, selectors, page }) => { - const dashboardPage = await gotoDashboardPage({ - uid: DASHBOARD_UID, - queryParams: new URLSearchParams({ editPanel: '1' }), - }); + // add a DataLink to the whole table, all cells will now have a single link. + const colCount = await page.getByRole('row').nth(1).getByRole('gridcell').count(); + for (let colIdx = 0; colIdx < colCount; colIdx++) { + // - pills column currently does not support DataLinks. + // - we don't apply DataLinks to the DataLinks column itself, since they're rendered inside. + if (colIdx === pillColIdx || colIdx === dataLinkColIdx) { + continue; + } - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) - ).toBeVisible(); + const cell = await getCell(page, 1, colIdx); + await expect(cell.locator('a')).toBeVisible(); + expect(cell.locator('a')).toHaveAttribute('href'); + expect(cell.locator('a')).not.toHaveAttribute('aria-haspopup', 'menu'); + } - // click the "State" column header to sort it. - const stateColumnHeader = await getCell(page, 0, 1); + const headerContainer = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer); - await stateColumnHeader.getByText('Info').click(); - await expect(stateColumnHeader).toHaveAttribute('aria-sort', 'ascending'); - expect(getCell(page, 1, 1)).resolves.toContainText('down'); // down or down fast + // add another data link. now we'll check that the multi-link popups work. + await addDataLink('Another test link', 'https://grafana.com/foo'); - await stateColumnHeader.getByText('Info').click(); - await expect(stateColumnHeader).toHaveAttribute('aria-sort', 'descending'); - expect(getCell(page, 1, 1)).resolves.toContainText('up'); // up or up fast - - await stateColumnHeader.getByText('Info').click(); - await expect(stateColumnHeader).not.toHaveAttribute('aria-sort'); - }); - - test('Tests filtering within a column', async ({ gotoDashboardPage, selectors, page }) => { - const dashboardPage = await gotoDashboardPage({ - uid: DASHBOARD_UID, - queryParams: new URLSearchParams({ editPanel: '1' }), - }); - - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) - ).toBeVisible(); - - await waitForTableLoad(page); - - const infoColumnIdx = await getColumnIdx(page, 'Info'); - - const stateColumnHeader = page.getByRole('columnheader').nth(infoColumnIdx); - - // get the first value in the "State" column, filter it out, then check that it went away. - const firstStateValue = (await (await getCell(page, 1, infoColumnIdx)).textContent())!; - await stateColumnHeader - .getByTestId(selectors.components.Panels.Visualization.TableNG.Filters.HeaderButton) - .click(); - const filterContainer = dashboardPage.getByGrafanaSelector( - selectors.components.Panels.Visualization.TableNG.Filters.Container - ); - - await expect(filterContainer).toBeVisible(); - - // select all, then click the first value to unselect it, filtering it out. - await filterContainer.getByTestId(selectors.components.Panels.Visualization.TableNG.Filters.SelectAll).click(); - await filterContainer.getByTitle(firstStateValue, { exact: true }).locator('label').click(); - await filterContainer.getByRole('button', { name: 'Ok' }).click(); - - // make sure the filter container closed when we clicked "Ok". - await expect(filterContainer).not.toBeVisible(); - - // did it actually filter out our value? - await expect(getCell(page, 1, infoColumnIdx)).resolves.not.toHaveText(firstStateValue); - }); - - test('Tests pagination, row height adjustment', async ({ gotoDashboardPage, selectors, page }) => { - const rowRe = /([\d]+) - ([\d]+) of ([\d]+) rows/; - const getRowStatus = async (page: Page | Locator) => { - const text = (await page.getByText(rowRe).textContent()) ?? ''; - const match = text.match(rowRe); - return { - start: parseInt(match?.[1] ?? '0', 10), - end: parseInt(match?.[2] ?? '0', 10), - total: parseInt(match?.[3] ?? '0', 10), - }; - }; - - const dashboardPage = await gotoDashboardPage({ - uid: DASHBOARD_UID, - queryParams: new URLSearchParams({ editPanel: '1' }), - }); - - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) - ).toBeVisible(); - - await page - .getByLabel(selectors.components.PanelEditor.OptionsPane.fieldLabel(`Enable pagination`), { exact: true }) - .click(); - - // because of text wrapping, we're guaranteed to only be showing a single row when we enable pagination. - await expect(page.getByText(/([\d]+) - ([\d]+) of ([\d]+) rows/)).toBeVisible(); - - // disable text wrap and see the number of rows. - await dashboardPage - .getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Wrap text')) - .last() - .click(); - - // any number of rows that is not "1" is allowed here, we don't want to police the exact number of rows that - // are rendered since there are tons of factors which could effect this. we do want to grab this number for comparison - // in a second, though. - const smallRowStatus = await getRowStatus(page); - expect(smallRowStatus.end).toBeGreaterThan(1); - expect(page.getByRole('grid').getByRole('row')).toHaveCount(smallRowStatus.end + 1); - - // change cell height to Large - await dashboardPage - .getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Table Cell height')) - .locator('input') - .last() - .click(); - const largeRowStatus = await getRowStatus(page); - expect(largeRowStatus.end).toBeLessThan(smallRowStatus.end); - expect(page.getByRole('grid').getByRole('row')).toHaveCount(largeRowStatus.end + 1); - - // click a page over with the directional nav - await page.getByLabel('next page').click(); - const nextPageStatus = await getRowStatus(page); - expect(nextPageStatus.start).toBe(largeRowStatus.end + 1); - expect(nextPageStatus.end).toBe(largeRowStatus.end * 2); - expect(nextPageStatus.total).toBe(largeRowStatus.total); - - // click a page number - await page - .getByTestId('data-testid panel content') - .getByRole('navigation') - .getByText('4', { exact: true }) - .click(); - const fourthPageStatus = await getRowStatus(page); - expect(fourthPageStatus.start).toBe(largeRowStatus.end * 3 + 1); - expect(fourthPageStatus.end).toBe(largeRowStatus.end * 4); - expect(fourthPageStatus.total).toBe(largeRowStatus.total); - }); - - // TODO: skipping this test for now due to flakiness in adding DataLinks. - test.skip('Tests DataLinks (single and multi) and actions', async ({ gotoDashboardPage, selectors, page }) => { - const addDataLink = async (title: string, url: string) => { - await dashboardPage - .getByGrafanaSelector( - selectors.components.PanelEditor.OptionsPane.fieldLabel('Data links and actions Data links') - ) - .locator('button') - .filter({ hasText: 'Add link' }) - .click(); - - // DataLinks dialog has popped open - fill it in and add a global datalink. - await expect(page.getByRole('dialog')).toBeVisible(); - await page.getByRole('dialog').locator('#link-title').fill(title); - await page.getByRole('dialog').locator('#data-link-input [contenteditable="true"]').focus(); - await page.getByRole('dialog').locator('#data-link-input [contenteditable="true"]').fill(url); - await page.getByRole('dialog').locator('#data-link-input [contenteditable="true"]').blur(); - await page.getByRole('dialog').locator('button[aria-disabled="false"]').filter({ hasText: 'Save' }).click(); - await expect(page.getByRole('dialog')).not.toBeVisible(); - }; - - const dashboardPage = await gotoDashboardPage({ - uid: DASHBOARD_UID, - queryParams: new URLSearchParams({ editPanel: '1' }), - }); - - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) - ).toBeVisible(); - - // disable text wrapping for this test to make it easier to click the links, the long lorem ipsum - // can push the links off the screen. - await dashboardPage - .getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Wrap text')) - .last() - .click(); - - const infoColumnIdx = await getColumnIdx(page, 'Info'); - const pillColIdx = await getColumnIdx(page, 'Pills'); - const dataLinkColIdx = await getColumnIdx(page, 'Data Link'); - - // Info column has a single DataLink by default. - const infoCell = await getCell(page, 1, infoColumnIdx); - await expect(infoCell.locator('a')).toBeVisible(); - expect(infoCell.locator('a')).toHaveAttribute('href'); - expect(infoCell.locator('a')).not.toHaveAttribute('aria-haspopup'); - - // now, add a DataLink to the whole table - await addDataLink('Test link', 'https://grafana.com'); - - // add a DataLink to the whole table, all cells will now have a single link. - const colCount = await page.getByRole('row').nth(1).getByRole('gridcell').count(); - for (let colIdx = 0; colIdx < colCount; colIdx++) { - // - pills column currently does not support DataLinks. - // - we don't apply DataLinks to the DataLinks column itself, since they're rendered inside. - if (colIdx === pillColIdx || colIdx === dataLinkColIdx) { - continue; - } - - const cell = await getCell(page, 1, colIdx); - await expect(cell.locator('a')).toBeVisible(); - expect(cell.locator('a')).toHaveAttribute('href'); + // loop thru the columns, click the links, observe that the tooltip appears, and close the tooltip. + for (let colIdx = 0; colIdx < colCount; colIdx++) { + const cell = await getCell(page, 1, colIdx); + if (colIdx === infoColumnIdx) { + // the Info column should still have its single link. expect(cell.locator('a')).not.toHaveAttribute('aria-haspopup', 'menu'); + continue; } - const headerContainer = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer); - - // add another data link. now we'll check that the multi-link popups work. - await addDataLink('Another test link', 'https://grafana.com/foo'); - - // loop thru the columns, click the links, observe that the tooltip appears, and close the tooltip. - for (let colIdx = 0; colIdx < colCount; colIdx++) { - const cell = await getCell(page, 1, colIdx); - if (colIdx === infoColumnIdx) { - // the Info column should still have its single link. - expect(cell.locator('a')).not.toHaveAttribute('aria-haspopup', 'menu'); - continue; - } - - // - pills column currently does not support DataLinks. - // - we don't apply DataLinks to the DataLinks column itself, since they're rendered inside. - if (colIdx === pillColIdx || colIdx === dataLinkColIdx) { - continue; - } - - await cell.locator('a').click({ force: true }); - await expect(page.getByTestId(selectors.components.DataLinksActionsTooltip.tooltipWrapper)).toBeVisible(); - - await headerContainer.click(); // convenient just to click the header to close the tooltip. - await expect(page.getByTestId(selectors.components.DataLinksActionsTooltip.tooltipWrapper)).not.toBeVisible(); + // - pills column currently does not support DataLinks. + // - we don't apply DataLinks to the DataLinks column itself, since they're rendered inside. + if (colIdx === pillColIdx || colIdx === dataLinkColIdx) { + continue; } - // add an Action to the whole table and check that the action button is added to the tooltip. - // TODO -- saving for another day. + await cell.locator('a').click({ force: true }); + await expect(page.getByTestId(selectors.components.DataLinksActionsTooltip.tooltipWrapper)).toBeVisible(); + + await headerContainer.click(); // convenient just to click the header to close the tooltip. + await expect(page.getByTestId(selectors.components.DataLinksActionsTooltip.tooltipWrapper)).not.toBeVisible(); + } + + // add an Action to the whole table and check that the action button is added to the tooltip. + // TODO -- saving for another day. + }); + + test('Empty Table panel', async ({ gotoDashboardPage, selectors }) => { + const dashboardPage = await gotoDashboardPage({ + uid: DASHBOARD_UID, + queryParams: new URLSearchParams({ editPanel: '2' }), }); - test('Empty Table panel', async ({ gotoDashboardPage, selectors }) => { - const dashboardPage = await gotoDashboardPage({ - uid: DASHBOARD_UID, - queryParams: new URLSearchParams({ editPanel: '2' }), - }); - - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.PanelDataErrorMessage) - ).toBeVisible(); - await expect( - dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) - ).not.toBeVisible(); - }); - } -); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.PanelDataErrorMessage) + ).toBeVisible(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink')) + ).not.toBeVisible(); + }); +}); diff --git a/e2e-playwright/panels-suite/table-markdown.spec.ts b/e2e-playwright/panels-suite/table-markdown.spec.ts new file mode 100644 index 00000000000..b2e127fc92c --- /dev/null +++ b/e2e-playwright/panels-suite/table-markdown.spec.ts @@ -0,0 +1,25 @@ +import { test, expect } from '@grafana/plugin-e2e'; + +test.use({ + viewport: { width: 1280, height: 1080 }, + featureToggles: { + tableNextGen: true, + }, +}); + +test.describe( + 'Panels test: Table - Markdown', + { + tag: ['@panels', '@table'], + }, + () => { + test('Tests Markdown tables are successfully rendered', async ({ gotoDashboardPage, page }) => { + await gotoDashboardPage({ + uid: '2769f5d8-0094-4ac4-a4f0-f68f620339cc', + queryParams: new URLSearchParams({ editPanel: '1' }), + }); + + await expect(page.getByRole('grid')).toBeVisible(); + }); + } +); diff --git a/e2e-playwright/panels-suite/table-sparkline.spec.ts b/e2e-playwright/panels-suite/table-sparkline.spec.ts index 16dc55f3df4..d2ac814e2df 100644 --- a/e2e-playwright/panels-suite/table-sparkline.spec.ts +++ b/e2e-playwright/panels-suite/table-sparkline.spec.ts @@ -1,29 +1,18 @@ import { test, expect } from '@grafana/plugin-e2e'; -test.use({ - viewport: { width: 1280, height: 1080 }, - featureToggles: { - tableNextGen: true, - }, -}); +test.use({ viewport: { width: 1280, height: 1080 }, featureToggles: { tableNextGen: true } }); -test.describe( - 'Panels test: Table - Sparkline', - { - tag: ['@panels'], - }, - () => { - test('Tests sparkline tables are successfully rendered', async ({ gotoDashboardPage, selectors, page }) => { - await gotoDashboardPage({ - uid: 'd6373b49-1957-4f00-9218-ee2120d3ecd9', - queryParams: new URLSearchParams({ editPanel: '2' }), - }); - - await expect(page.getByRole('grid')).toBeVisible(); - - const uplotCount = await page.locator('.uplot').count(); - const rowCount = await page.getByRole('row').count(); - expect(uplotCount).toBe(rowCount - 1); +test.describe('Panels test: Table - Sparkline', { tag: ['@panels', '@table'] }, () => { + test('Tests sparkline tables are successfully rendered', async ({ gotoDashboardPage, selectors, page }) => { + await gotoDashboardPage({ + uid: 'd6373b49-1957-4f00-9218-ee2120d3ecd9', + queryParams: new URLSearchParams({ editPanel: '2' }), }); - } -); + + await expect(page.getByRole('grid')).toBeVisible(); + + const uplotCount = await page.locator('.uplot').count(); + const rowCount = await page.getByRole('row').count(); + expect(uplotCount).toBe(rowCount - 1); + }); +}); diff --git a/e2e/dashboards-search-suite/mode0.ini b/e2e/dashboards-search-suite/mode0.ini index 1e0c6c55b0a..e8f0a768c77 100644 --- a/e2e/dashboards-search-suite/mode0.ini +++ b/e2e/dashboards-search-suite/mode0.ini @@ -1,14 +1,10 @@ [server] [feature_toggles] -kubernetesFolders = true unifiedStorageSearch = true unifiedStorageSearchUI = true grafanaAPIServerWithExperimentalAPIs = true -kubernetesDashboardsAPI = true -kubernetesCliDashboards = true unifiedStorageSearchSprinkles = true -kubernetesFoldersServiceV2 = true unifiedStorageSearchPermissionFiltering = true [unified_storage.folders.folder.grafana.app] diff --git a/e2e/dashboards-search-suite/mode1.ini b/e2e/dashboards-search-suite/mode1.ini index 56e16cb4cff..5beecc31f65 100644 --- a/e2e/dashboards-search-suite/mode1.ini +++ b/e2e/dashboards-search-suite/mode1.ini @@ -1,14 +1,10 @@ [server] [feature_toggles] -kubernetesFolders = true unifiedStorageSearch = true unifiedStorageSearchUI = true grafanaAPIServerWithExperimentalAPIs = true -kubernetesDashboardsAPI = true -kubernetesCliDashboards = true unifiedStorageSearchSprinkles = true -kubernetesFoldersServiceV2 = true unifiedStorageSearchPermissionFiltering = true [unified_storage.folders.folder.grafana.app] diff --git a/e2e/dashboards-search-suite/mode2-legacy-search-api.ini b/e2e/dashboards-search-suite/mode2-legacy-search-api.ini index 4b671c8b78b..45b16de4034 100644 --- a/e2e/dashboards-search-suite/mode2-legacy-search-api.ini +++ b/e2e/dashboards-search-suite/mode2-legacy-search-api.ini @@ -1,14 +1,10 @@ [server] [feature_toggles] -kubernetesFolders = true unifiedStorageSearch = true unifiedStorageSearchUI = false grafanaAPIServerWithExperimentalAPIs = true -kubernetesDashboardsAPI = true -kubernetesCliDashboards = true unifiedStorageSearchSprinkles = true -kubernetesFoldersServiceV2 = true unifiedStorageSearchPermissionFiltering = true [unified_storage.folders.folder.grafana.app] diff --git a/e2e/dashboards-search-suite/mode2.ini b/e2e/dashboards-search-suite/mode2.ini index af96c552def..55538ee82e3 100644 --- a/e2e/dashboards-search-suite/mode2.ini +++ b/e2e/dashboards-search-suite/mode2.ini @@ -1,14 +1,10 @@ [server] [feature_toggles] -kubernetesFolders = true unifiedStorageSearch = true unifiedStorageSearchUI = true grafanaAPIServerWithExperimentalAPIs = true -kubernetesDashboardsAPI = true -kubernetesCliDashboards = true unifiedStorageSearchSprinkles = true -kubernetesFoldersServiceV2 = true unifiedStorageSearchPermissionFiltering = true [unified_storage.folders.folder.grafana.app] diff --git a/e2e/dashboards-search-suite/mode3.ini b/e2e/dashboards-search-suite/mode3.ini index a4a56f9d80c..5955dfcd72a 100644 --- a/e2e/dashboards-search-suite/mode3.ini +++ b/e2e/dashboards-search-suite/mode3.ini @@ -1,14 +1,10 @@ [server] [feature_toggles] -kubernetesFolders = true unifiedStorageSearch = true unifiedStorageSearchUI = true grafanaAPIServerWithExperimentalAPIs = true -kubernetesDashboardsAPI = true -kubernetesCliDashboards = true unifiedStorageSearchSprinkles = true -kubernetesFoldersServiceV2 = true unifiedStorageSearchPermissionFiltering = true [unified_storage.folders.folder.grafana.app] diff --git a/e2e/dashboards-search-suite/mode4.ini b/e2e/dashboards-search-suite/mode4.ini index dd774cb2f14..e0f6f6d892d 100644 --- a/e2e/dashboards-search-suite/mode4.ini +++ b/e2e/dashboards-search-suite/mode4.ini @@ -1,14 +1,10 @@ [server] [feature_toggles] -kubernetesFolders = true unifiedStorageSearch = true unifiedStorageSearchUI = true grafanaAPIServerWithExperimentalAPIs = true -kubernetesDashboardsAPI = true -kubernetesCliDashboards = true unifiedStorageSearchSprinkles = true -kubernetesFoldersServiceV2 = true unifiedStorageSearchPermissionFiltering = true [unified_storage.folders.folder.grafana.app] diff --git a/e2e/dashboards-search-suite/mode5.ini b/e2e/dashboards-search-suite/mode5.ini index 83437bda185..8722cb091eb 100644 --- a/e2e/dashboards-search-suite/mode5.ini +++ b/e2e/dashboards-search-suite/mode5.ini @@ -1,14 +1,10 @@ [server] [feature_toggles] -kubernetesFolders = true unifiedStorageSearch = true unifiedStorageSearchUI = true grafanaAPIServerWithExperimentalAPIs = true -kubernetesDashboardsAPI = true -kubernetesCliDashboards = true unifiedStorageSearchSprinkles = true -kubernetesFoldersServiceV2 = true unifiedStorageSearchPermissionFiltering = true [unified_storage.folders.folder.grafana.app] diff --git a/e2e/dashboards-suite/Repeating_a_panel_horizontally.spec.ts b/e2e/dashboards-suite/Repeating_a_panel_horizontally.spec.ts deleted file mode 100644 index 9b3ec4806e0..00000000000 --- a/e2e/dashboards-suite/Repeating_a_panel_horizontally.spec.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { e2e } from '../utils'; -const PAGE_UNDER_TEST = 'WVpf2jp7z/repeating-a-panel-horizontally'; - -describe('Repeating a panel horizontally', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('should be able to repeat a panel horizontally', () => { - e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST }); - let prevLeft = Number.NEGATIVE_INFINITY; - let prevTop: number | null = null; - const panelTitles = ['Panel Title 1', 'Panel Title 2', 'Panel Title 3']; - panelTitles.forEach((title) => { - e2e.components.Panels.Panel.title(title) - .should('be.visible') - .then(($el) => { - const { left, top } = $el[0].getBoundingClientRect(); - expect(left).to.be.greaterThan(prevLeft); - if (prevTop !== null) { - expect(top).to.be.equal(prevTop); - } - - prevLeft = left; - prevTop = top; - }); - }); - }); - - it('responds to changes to the variables', () => { - e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST }); - let prevLeft = Number.NEGATIVE_INFINITY; - let prevTop: number | null = null; - const panelTitles = ['Panel Title 1', 'Panel Title 2', 'Panel Title 3']; - panelTitles.forEach((title) => { - e2e.components.Panels.Panel.title(title).should('be.visible'); - }); - - // Change to only show panels 1 + 3 - e2e.pages.Dashboard.SubMenu.submenuItemLabels('horizontal') - .parent() - .within(() => { - cy.get('input').click(); - }); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('1').click(); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('3').click(); - - // blur the dropdown - cy.get('body').click(); - - const panelsShown = ['Panel Title 1', 'Panel Title 3']; - const panelsNotShown = ['Panel Title 2']; - panelsShown.forEach((title) => { - e2e.components.Panels.Panel.title(title) - .should('be.visible') - .then(($el) => { - const { left, top } = $el[0].getBoundingClientRect(); - expect(left).to.be.greaterThan(prevLeft); - if (prevTop !== null) { - expect(top).to.be.equal(prevTop); - } - - prevLeft = left; - prevTop = top; - }); - }); - panelsNotShown.forEach((title) => { - e2e.components.Panels.Panel.title(title).should('not.exist'); - }); - }); - - it('loads a dashboard based on the query params correctly', () => { - // Have to manually add the queryParams to the url because they have the same name - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?var-horizontal=1&var-horizontal=3` }); - let prevLeft = Number.NEGATIVE_INFINITY; - let prevTop: number | null = null; - const panelsShown = ['Panel Title 1', 'Panel Title 3']; - const panelsNotShown = ['Panel Title 2']; - // Check correct panels are displayed - panelsShown.forEach((title) => { - e2e.components.Panels.Panel.title(title) - .should('be.visible') - .then(($el) => { - const { left, top } = $el[0].getBoundingClientRect(); - expect(left).to.be.greaterThan(prevLeft); - if (prevTop !== null) { - expect(top).to.be.equal(prevTop); - } - - prevLeft = left; - prevTop = top; - }); - }); - panelsNotShown.forEach((title) => { - e2e.components.Panels.Panel.title(title).should('not.exist'); - }); - }); -}); diff --git a/e2e/dashboards-suite/Repeating_a_panel_vertically.spec.ts b/e2e/dashboards-suite/Repeating_a_panel_vertically.spec.ts deleted file mode 100644 index fbf0f5bf9d2..00000000000 --- a/e2e/dashboards-suite/Repeating_a_panel_vertically.spec.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { e2e } from '../utils'; -const PAGE_UNDER_TEST = 'OY8Ghjt7k/repeating-a-panel-vertically'; - -describe('Repeating a panel vertically', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('should be able to repeat a panel vertically', () => { - e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST }); - - let prevTop = Number.NEGATIVE_INFINITY; - let prevLeft: number | null = null; - const panelTitles = ['Panel Title 1', 'Panel Title 2', 'Panel Title 3']; - panelTitles.forEach((title) => { - e2e.components.Panels.Panel.title(title) - .should('be.visible') - .then(($el) => { - const { left, top } = $el[0].getBoundingClientRect(); - expect(top).to.be.greaterThan(prevTop); - if (prevLeft !== null) { - expect(left).to.be.equal(prevLeft); - } - prevLeft = left; - prevTop = top; - }); - }); - }); - - it('responds to changes to the variables', () => { - e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST }); - - let prevTop = Number.NEGATIVE_INFINITY; - let prevLeft: number | null = null; - const panelTitles = ['Panel Title 1', 'Panel Title 2', 'Panel Title 3']; - panelTitles.forEach((title) => { - e2e.components.Panels.Panel.title(title).should('be.visible'); - }); - - // Change to only show panels 1 + 3 - e2e.pages.Dashboard.SubMenu.submenuItemLabels('vertical') - .parent() - .within(() => { - cy.get('input').click(); - }); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('1').click(); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('3').click(); - - // blur the dropdown - cy.get('body').click(); - - const panelsShown = ['Panel Title 1', 'Panel Title 3']; - const panelsNotShown = ['Panel Title 2']; - panelsShown.forEach((title) => { - e2e.components.Panels.Panel.title(title) - .should('be.visible') - .then(($el) => { - const { left, top } = $el[0].getBoundingClientRect(); - expect(top).to.be.greaterThan(prevTop); - if (prevLeft !== null) { - expect(left).to.be.equal(prevLeft); - } - prevLeft = left; - prevTop = top; - }); - }); - panelsNotShown.forEach((title) => { - e2e.components.Panels.Panel.title(title).should('not.exist'); - }); - }); - - it('loads a dashboard based on the query params correctly', () => { - // Have to manually add the queryParams to the url because they have the same name - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?var-vertical=1&var-vertical=3` }); - - let prevTop = Number.NEGATIVE_INFINITY; - let prevLeft: number | null = null; - const panelsShown = ['Panel Title 1', 'Panel Title 3']; - const panelsNotShown = ['Panel Title 2']; - panelsShown.forEach((title) => { - e2e.components.Panels.Panel.title(title) - .should('be.visible') - .then(($el) => { - const { left, top } = $el[0].getBoundingClientRect(); - expect(top).to.be.greaterThan(prevTop); - if (prevLeft !== null) { - expect(left).to.be.equal(prevLeft); - } - prevLeft = left; - prevTop = top; - }); - }); - panelsNotShown.forEach((title) => { - e2e.components.Panels.Panel.title(title).should('not.exist'); - }); - }); -}); diff --git a/e2e/dashboards-suite/Repeating_an_empty_row.spec.ts b/e2e/dashboards-suite/Repeating_an_empty_row.spec.ts deleted file mode 100644 index 2bf52250bad..00000000000 --- a/e2e/dashboards-suite/Repeating_an_empty_row.spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { e2e } from '../utils'; -const PAGE_UNDER_TEST = 'dtpl2Ctnk/repeating-an-empty-row'; - -describe('Repeating empty rows', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('should be able to repeat empty rows vertically', () => { - e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST }); - - let prevTop = Number.NEGATIVE_INFINITY; - const rowTitles = ['Row title 1', 'Row title 2', 'Row title 3']; - rowTitles.forEach((title) => { - e2e.components.DashboardRow.title(title) - .should('be.visible') - .then(($el) => { - const { top } = $el[0].getBoundingClientRect(); - expect(top).to.be.greaterThan(prevTop); - prevTop = top; - }); - }); - }); - - it('responds to changes to the variables', () => { - e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST }); - - let prevTop = Number.NEGATIVE_INFINITY; - const rowTitles = ['Row title 1', 'Row title 2', 'Row title 3']; - - rowTitles.forEach((title) => { - e2e.components.DashboardRow.title(title).should('be.visible'); - }); - - e2e.pages.Dashboard.SubMenu.submenuItemLabels('row') - .parent() - .within(() => { - cy.get('input').click(); - }); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('1').click(); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('3').click(); - - // blur the dropdown - cy.get('body').click(); - - const rowsShown = ['Row title 1', 'Row title 3']; - const rowsNotShown = ['Row title 2']; - rowsShown.forEach((title) => { - e2e.components.DashboardRow.title(title) - .should('be.visible') - .then(($el) => { - const { top } = $el[0].getBoundingClientRect(); - expect(top).to.be.greaterThan(prevTop); - prevTop = top; - }); - }); - - rowsNotShown.forEach((title) => { - e2e.components.DashboardRow.title(title).should('not.exist'); - }); - }); - - it('loads a dashboard based on the query params correctly', () => { - // Have to manually add the queryParams to the url because they have the same name - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?var-row=1&var-row=3` }); - - let prevTop = Number.NEGATIVE_INFINITY; - const rowsShown = ['Row title 1', 'Row title 3']; - const rowsNotShown = ['Row title 2']; - rowsShown.forEach((title) => { - e2e.components.DashboardRow.title(title) - .should('be.visible') - .then(($el) => { - const { top } = $el[0].getBoundingClientRect(); - expect(top).to.be.greaterThan(prevTop); - prevTop = top; - }); - }); - - rowsNotShown.forEach((title) => { - e2e.components.DashboardRow.title(title).should('not.exist'); - }); - }); -}); diff --git a/e2e/dashboards-suite/dashboard-browse-nested.spec.ts b/e2e/dashboards-suite/dashboard-browse-nested.spec.ts deleted file mode 100644 index a18c4108e4e..00000000000 --- a/e2e/dashboards-suite/dashboard-browse-nested.spec.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { e2e } from '../utils'; - -import { makeNewDashboardRequestBody } from './utils/makeDashboard'; - -const NUM_ROOT_FOLDERS = 60; -const NUM_ROOT_DASHBOARDS = 60; -const NUM_NESTED_FOLDERS = 60; -const NUM_NESTED_DASHBOARDS = 60; - -// TODO enable this test when nested folders goes live -describe.skip('Dashboard browse (nested)', () => { - const dashboardUIDsToCleanUp: string[] = []; - const folderUIDsToCleanUp: string[] = []; - - // Add nested folder structure - before(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'), false); - - // Add root folders - for (let i = 0; i < NUM_ROOT_FOLDERS; i++) { - cy.request({ - method: 'POST', - url: '/api/folders', - body: { - title: `Root folder ${i.toString().padStart(2, '0')}`, - }, - headers: { - 'Content-Type': 'application/json', - }, - }).then((response) => { - folderUIDsToCleanUp.push(response.body.uid); - }); - } - - // Add root dashboards - for (let i = 0; i < NUM_ROOT_DASHBOARDS; i++) { - cy.request({ - method: 'POST', - url: '/api/dashboards/db', - body: makeNewDashboardRequestBody(`Root dashboard ${i.toString().padStart(2, '0')}`), - headers: { - 'Content-Type': 'application/json', - }, - }).then((response) => { - dashboardUIDsToCleanUp.push(response.body.uid); - }); - } - - // Add folder with children - cy.request({ - method: 'POST', - url: '/api/folders', - body: { - title: 'A root folder with children', - }, - headers: { - 'Content-Type': 'application/json', - }, - }).then((response) => { - const folderUid = response.body.uid; - folderUIDsToCleanUp.push(folderUid); - // Add nested folders - for (let i = 0; i < NUM_NESTED_FOLDERS; i++) { - cy.request({ - method: 'POST', - url: '/api/folders', - body: { - title: `Nested folder ${i.toString().padStart(2, '0')}`, - parentUid: folderUid, - }, - headers: { - 'Content-Type': 'application/json', - }, - }); - } - // Add nested dashboards - for (let i = 0; i < NUM_NESTED_DASHBOARDS; i++) { - cy.request({ - method: 'POST', - url: '/api/dashboards/db', - body: makeNewDashboardRequestBody(`Nested dashboard ${i.toString().padStart(2, '0')}`, folderUid), - headers: { - 'Content-Type': 'application/json', - }, - }); - } - }); - }); - - // Remove nested folder structure - after(() => { - // Clean up root dashboards - for (const dashboardUID of dashboardUIDsToCleanUp) { - e2e.flows.deleteDashboard({ - uid: dashboardUID, - quick: true, - title: '', - }); - } - // Clean up root folders (cascading delete will remove any nested folders and dashboards) - for (const folderUID of folderUIDsToCleanUp) { - cy.request({ - method: 'DELETE', - url: `/api/folders/${folderUID}`, - qs: { - forceDeleteRules: false, - }, - }); - } - }); - - it('pagination works correctly for folders and root', () => { - e2e.pages.Dashboards.visit(); - - cy.contains('A root folder with children').should('be.visible'); - - // Expand A root folder with children - cy.get('[aria-label="Expand folder A root folder with children"]').click(); - cy.contains('Nested folder 00').should('be.visible'); - - // Scroll the page and check visibility of next set of items - e2e.pages.BrowseDashboards.table.body().find('> div').scrollTo(0, 1700); - cy.contains('Nested folder 59').should('be.visible'); - cy.contains('Nested dashboard 00').should('be.visible'); - - // Scroll the page and check visibility of next set of items - e2e.pages.BrowseDashboards.table.body().find('> div').scrollTo(0, 3800); - cy.contains('Nested dashboard 59').should('be.visible'); - cy.contains('Root folder 00').should('be.visible'); - - // Scroll the page and check visibility of next set of items - e2e.pages.BrowseDashboards.table.body().find('> div').scrollTo(0, 5900); - cy.contains('Root folder 59').should('be.visible'); - cy.contains('Root dashboard 00').should('be.visible'); - - // Scroll the page and check visibility of next set of items - e2e.pages.BrowseDashboards.table.body().find('> div').scrollTo(0, 8000); - cy.contains('Root dashboard 59').should('be.visible'); - }); -}); diff --git a/e2e/dashboards-suite/dashboard-browse.spec.ts b/e2e/dashboards-suite/dashboard-browse.spec.ts deleted file mode 100644 index a5dc3cfc5ca..00000000000 --- a/e2e/dashboards-suite/dashboard-browse.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import testDashboard from '../dashboards/TestDashboard.json'; -import { e2e } from '../utils'; -// Skipping due to race conditions with same old arch test e2e/dashboards-suite/dashboard-browse.spec.ts -describe.skip('Dashboard browse', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Manage Dashboards tests', () => { - e2e.flows.importDashboard(testDashboard, 1000, true); - - e2e.pages.Dashboards.visit(); - - // Folders and dashboards should be visible - e2e.pages.BrowseDashboards.table.row('gdev dashboards').should('be.visible'); - e2e.pages.BrowseDashboards.table.row('E2E Test - Import Dashboard').should('be.visible'); - - // gdev dashboards folder is collapsed - its content should not be visible - e2e.pages.BrowseDashboards.table.row('Bar Gauge Demo').should('not.exist'); - - // should click a folder and see it's children - e2e.pages.BrowseDashboards.table.row('gdev dashboards').find('[aria-label^="Expand folder"]').click(); - e2e.pages.BrowseDashboards.table.row('Bar Gauge Demo').should('be.visible'); - - // Open the new folder drawer - cy.contains('button', 'New').click(); - cy.contains('button', 'New folder').click(); - - // And create a new folder - e2e.pages.BrowseDashboards.NewFolderForm.nameInput().type('My new folder'); - e2e.pages.BrowseDashboards.NewFolderForm.form().contains('button', 'Create').click(); - e2e.components.Alert.alertV2('success').find('button[aria-label="Close alert"]').click(); - cy.contains('h1', 'My new folder').should('be.visible'); - - // Delete the folder and expect to go back to the root - cy.contains('button', 'Folder actions').click(); - cy.contains('button', 'Delete').click(); - e2e.flows.confirmDelete(); - cy.contains('h1', 'Dashboards').should('be.visible'); - - // Can collapse the gdev folder and delete the dashboard we imported - e2e.pages.BrowseDashboards.table.row('gdev dashboards').find('[aria-label^="Collapse folder"]').click(); - e2e.pages.BrowseDashboards.table - .row('E2E Test - Import Dashboard') - .find('[type="checkbox"]') - .click({ force: true }); - - cy.contains('button', 'Delete').click(); - e2e.flows.confirmDelete(); - e2e.pages.BrowseDashboards.table.row('E2E Test - Import Dashboard').should('not.exist'); - }); -}); diff --git a/e2e/dashboards-suite/dashboard-export-json.spec.ts b/e2e/dashboards-suite/dashboard-export-json.spec.ts deleted file mode 100644 index 34abe6cec05..00000000000 --- a/e2e/dashboards-suite/dashboard-export-json.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { e2e } from '../utils'; -import '../utils/support/clipboard'; - -describe('Export as JSON', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Export for internal and external use', () => { - // Opening a dashboard - cy.intercept({ - pathname: '/api/ds/query', - }).as('query'); - e2e.flows.openDashboard({ - uid: 'ZqZnVvFZz', - queryParams: { '__feature.scenes': true, '__feature.newDashboardSharingComponent': true }, - }); - cy.wait('@query'); - - // cy.wrap( - // Cypress.automation('remote:debugger:protocol', { - // command: 'Browser.grantPermissions', - // params: { - // permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'], - // origin: window.location.origin, - // }, - // }) - // ); - - // Open the export drawer - e2e.pages.Dashboard.DashNav.NewExportButton.arrowMenu().click(); - e2e.pages.Dashboard.DashNav.NewExportButton.Menu.exportAsJson().click(); - - cy.url().should('include', 'shareView=export'); - - // Export as JSON - e2e.pages.ExportDashboardDrawer.ExportAsJson.container().should('be.visible'); - e2e.pages.ExportDashboardDrawer.ExportAsJson.exportExternallyToggle().should('not.be.checked'); - e2e.components.CodeEditor.container().should('exist'); - - e2e.pages.ExportDashboardDrawer.ExportAsJson.saveToFileButton().should('exist'); - e2e.pages.ExportDashboardDrawer.ExportAsJson.copyToClipboardButton().should('exist'); - e2e.pages.ExportDashboardDrawer.ExportAsJson.cancelButton().should('exist'); - - //TODO Failing in CI/CD. Fix it - // Copy link button should be visible - // e2e.pages.ExportDashboardDrawer.ExportAsJson.copyToClipboardButton() - // .click() - // .then(() => { - // cy.copyFromClipboard().then((url) => { - // cy.wrap(url).should('not.include', '__inputs'); - // }); - // }); - - e2e.pages.ExportDashboardDrawer.ExportAsJson.exportExternallyToggle().click({ force: true }); - - //TODO Failing in CI/CD. Fix it - // e2e.pages.ExportDashboardDrawer.ExportAsJson.copyToClipboardButton() - // .click() - // .then(() => { - // cy.copyFromClipboard().then((url) => { - // cy.wrap(url).should('include', '__inputs'); - // }); - // }); - - e2e.pages.ExportDashboardDrawer.ExportAsJson.cancelButton().click(); - - cy.url().should('not.include', 'shareView=export'); - }); -}); diff --git a/e2e/dashboards-suite/dashboard-keybindings.spec.ts b/e2e/dashboards-suite/dashboard-keybindings.spec.ts deleted file mode 100644 index 42070f1ebc4..00000000000 --- a/e2e/dashboards-suite/dashboard-keybindings.spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { e2e } from '../utils'; - -describe('Dashboard keybindings', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('should collapse and expand all rows', () => { - e2e.flows.openDashboard({ uid: 'Repeating-rows-uid/repeating-rows' }); - e2e.components.Panels.Panel.content().should('have.length', 5); - e2e.components.Panels.Panel.title('server = A, pod = Bob').should('be.visible'); - e2e.components.Panels.Panel.title('server = B, pod = Bob').should('be.visible'); - - cy.get('body').type('d').type('{shift}c'); - e2e.components.Panels.Panel.content().should('have.length', 0); - e2e.components.Panels.Panel.title('server = A, pod = Bob').should('not.exist'); - e2e.components.Panels.Panel.title('server = B, pod = Bob').should('not.exist'); - - cy.get('body').type('d').type('{shift}e'); - e2e.components.Panels.Panel.content().should('have.length', 6); - e2e.components.Panels.Panel.title('server = A, pod = Bob').should('be.visible'); - e2e.components.Panels.Panel.title('server = B, pod = Bob').should('be.visible'); - }); - - it('should open panel inspect', () => { - e2e.flows.openDashboard({ uid: 'edediimbjhdz4b/a-tall-dashboard' }); - e2e.components.Panels.Panel.title('Panel #1').type('i'); - e2e.components.PanelInspector.Json.content().should('be.visible'); - cy.get('body').type('{esc}'); - e2e.components.PanelInspector.Json.content().should('not.exist'); - }); -}); diff --git a/e2e/dashboards-suite/dashboard-links-without-slug.spec.ts b/e2e/dashboards-suite/dashboard-links-without-slug.spec.ts deleted file mode 100644 index defc7678d3b..00000000000 --- a/e2e/dashboards-suite/dashboard-links-without-slug.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import testDashboard from '../dashboards/DataLinkWithoutSlugTest.json'; -import { e2e } from '../utils'; - -describe('Dashboard with data links that have no slug', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Should not reload if linking to same dashboard', () => { - cy.intercept({ - pathname: '/api/ds/query', - }).as('query'); - - e2e.flows.importDashboard(testDashboard, 1000, true); - cy.wait('@query'); - - e2e.components.Panels.Panel.title('Data links without slug').should('exist'); - - e2e.components.DataLinksContextMenu.singleLink().contains('9yy21uzzxypg').click(); - cy.contains('Loading', { timeout: 500 }) - .should(() => {}) // prevent test from failing if it does not find loading - .then(throwIfLoadingFound); - cy.url().should('include', urlShouldContain); - - e2e.components.DataLinksContextMenu.singleLink().contains('dr199bpvpcru').click(); - cy.contains('Loading', { timeout: 500 }) - .should(() => {}) // prevent test from failing if it does not find loading - .then(throwIfLoadingFound); - cy.url().should('include', urlShouldContain); - - e2e.components.DataLinksContextMenu.singleLink().contains('dre33fzyxcrz').click(); - cy.contains('Loading', { timeout: 500 }) - .should(() => {}) // prevent test from failing if it does not find loading - .then(throwIfLoadingFound); - cy.url().should('include', urlShouldContain); - }); -}); - -const urlShouldContain = '/d/data-link-no-slug/data-link-without-slug-test'; - -const throwIfLoadingFound = (el: JQuery) => { - if (el.length) { - // This means dashboard refreshes when clicking self-referencing data link - // that has no slug in it - throw new Error('Should not contain Loading'); - } -}; diff --git a/e2e/dashboards-suite/dashboard-live-streaming.spec.ts b/e2e/dashboards-suite/dashboard-live-streaming.spec.ts deleted file mode 100644 index 0623c8c30c7..00000000000 --- a/e2e/dashboards-suite/dashboard-live-streaming.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import testDashboard from '../dashboards/DashboardLiveTest.json'; -import { e2e } from '../utils'; - -// Skipping due to flakiness/race conditions with same old arch test e2e/dashboards-suite/dashboard-live-streaming.spec.ts -describe.skip('Dashboard Live streaming support', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - e2e.flows.importDashboard(testDashboard, 1000); - }); - - it('Should receive streaming data', () => { - e2e.flows.openDashboard({ uid: 'live-e2e-test', queryParams: { '__feature.tableNextGen': false } }); - cy.wait(1000); - e2e.components.Panels.Panel.title('Live').should('exist'); - e2e.components.Panels.Visualization.Table.body().find('[role="row"]').should('have.length.at.least', 5); - }); -}); diff --git a/e2e/dashboards-suite/dashboard-public-create.spec.ts b/e2e/dashboards-suite/dashboard-public-create.spec.ts deleted file mode 100644 index 993bb82c702..00000000000 --- a/e2e/dashboards-suite/dashboard-public-create.spec.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { e2e } from '../utils'; -// Skipping due to race conditions with same old arch test e2e/dashboards-suite/dashboard-public-create.spec.ts -describe.skip('Public dashboards', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Create a public dashboard', () => { - // Opening a dashboard without template variables - cy.intercept({ - pathname: '/api/ds/query', - }).as('query'); - e2e.flows.openDashboard({ uid: 'ZqZnVvFZz' }); - cy.wait('@query'); - - // Open sharing modal - e2e.components.NavToolbar.shareDashboard().click(); - - // Select public dashboards tab - e2e.components.Tab.title('Public dashboard').click(); - - // Create button should be disabled - e2e.pages.ShareDashboardModal.PublicDashboard.CreateButton().should('be.disabled'); - - // Create flow shouldn't show these elements - e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput().should('not.exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlButton().should('not.exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.EnableAnnotationsSwitch().should('not.exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.EnableTimeRangeSwitch().should('not.exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.PauseSwitch().should('not.exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.DeleteButton().should('not.exist'); - - // Acknowledge checkboxes - e2e.pages.ShareDashboardModal.PublicDashboard.WillBePublicCheckbox().should('be.enabled').click({ force: true }); - e2e.pages.ShareDashboardModal.PublicDashboard.LimitedDSCheckbox().should('be.enabled').click({ force: true }); - e2e.pages.ShareDashboardModal.PublicDashboard.CostIncreaseCheckbox().should('be.enabled').click({ force: true }); - - e2e.pages.ShareDashboardModal.PublicDashboard.CreateButton().should('be.enabled'); - - // Create public dashboard - cy.intercept('POST', '/api/dashboards/uid/ZqZnVvFZz/public-dashboards').as('save'); - e2e.pages.ShareDashboardModal.PublicDashboard.CreateButton().click(); - cy.wait('@save'); - - // These elements shouldn't be rendered after creating public dashboard - e2e.pages.ShareDashboardModal.PublicDashboard.WillBePublicCheckbox().should('not.exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.LimitedDSCheckbox().should('not.exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.CostIncreaseCheckbox().should('not.exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.CreateButton().should('not.exist'); - - // These elements should be rendered - e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlButton().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.PauseSwitch().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.DeleteButton().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.SettingsDropdown().should('exist'); - - e2e.pages.ShareDashboardModal.PublicDashboard.SettingsDropdown().click(); - // There elements should be rendered once the Settings dropdown is opened - e2e.pages.ShareDashboardModal.PublicDashboard.EnableAnnotationsSwitch().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.EnableTimeRangeSwitch().should('exist'); - }); - - it('Open a public dashboard', () => { - // Opening a dashboard without template variables - cy.intercept({ - method: 'POST', - pathname: '/api/ds/query', - }).as('query'); - e2e.flows.openDashboard({ uid: 'ZqZnVvFZz' }); - cy.wait('@query'); - - // Tag indicating a dashboard is public - e2e.pages.Dashboard.DashNav.publicDashboardTag().should('exist'); - - // Open sharing modal - e2e.components.NavToolbar.shareDashboard().click(); - - // Select public dashboards tab - cy.intercept('GET', '/api/dashboards/uid/ZqZnVvFZz/public-dashboards').as('query-public-dashboard'); - e2e.components.Tab.title('Public dashboard').click(); - cy.wait('@query-public-dashboard'); - - e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlButton().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.PauseSwitch().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.DeleteButton().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.SettingsDropdown().should('exist'); - - e2e.pages.ShareDashboardModal.PublicDashboard.SettingsDropdown().click(); - // There elements should be rendered once the Settings dropdown is opened - e2e.pages.ShareDashboardModal.PublicDashboard.EnableTimeRangeSwitch().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.EnableAnnotationsSwitch().should('exist'); - - // Make a request to public dashboards api endpoint without authentication - e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput() - .invoke('val') - .then((url) => { - cy.clearCookies() - .request(getPublicDashboardAPIUrl(String(url))) - .then((resp) => { - expect(resp.status).to.eq(200); - }); - }); - }); - - it('Disable a public dashboard', () => { - // Opening a dashboard without template variables - cy.intercept({ - method: 'POST', - pathname: '/api/ds/query', - }).as('query'); - e2e.flows.openDashboard({ uid: 'ZqZnVvFZz' }); - cy.wait('@query'); - - // Open sharing modal - e2e.components.NavToolbar.shareDashboard().click(); - - // Select public dashboards tab - cy.intercept('GET', '/api/dashboards/uid/ZqZnVvFZz/public-dashboards').as('query-public-dashboard'); - e2e.components.Tab.title('Public dashboard').click(); - cy.wait('@query-public-dashboard'); - - // save url before disabling public dashboard - e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput() - .invoke('val') - .then((text) => cy.wrap(text).as('url')); - - // Save public dashboard - cy.intercept('PATCH', '/api/dashboards/uid/ZqZnVvFZz/public-dashboards/*').as('update'); - // Switch off enabling toggle - e2e.pages.ShareDashboardModal.PublicDashboard.PauseSwitch().should('be.enabled').click({ force: true }); - cy.wait('@update'); - - // Url should be hidden - e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput().should('be.disabled'); - e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlButton().should('be.disabled'); - - // Make a request to public dashboards api endpoint without authentication - cy.get('@url').then((url) => { - cy.clearCookies() - .request({ url: getPublicDashboardAPIUrl(String(url)), failOnStatusCode: false }) - .then((resp) => { - expect(resp.status).to.eq(403); - }); - }); - }); -}); - -const getPublicDashboardAPIUrl = (url: string): string => { - let accessToken = url.split('/').pop(); - return `/api/public/dashboards/${accessToken}`; -}; diff --git a/e2e/dashboards-suite/dashboard-public-templating.spec.ts b/e2e/dashboards-suite/dashboard-public-templating.spec.ts deleted file mode 100644 index 8040f4ce56e..00000000000 --- a/e2e/dashboards-suite/dashboard-public-templating.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { e2e } from '../utils'; - -describe('Create a public dashboard with template variables shows a template variable warning', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Create a public dashboard with template variables shows a template variable warning', () => { - // Opening a dashboard with template variables - e2e.flows.openDashboard({ uid: 'HYaGDGIMk', queryParams: { '__feature.newDashboardSharingComponent': false } }); - - // Open sharing modal - e2e.components.NavToolbar.shareDashboard().click(); - - // Select public dashboards tab - e2e.components.Tab.title('Public Dashboard').click(); - - // Warning Alert dashboard cannot be made public because it has template variables - e2e.pages.ShareDashboardModal.PublicDashboard.TemplateVariablesWarningAlert().should('be.visible'); - - // Configuration elements for public dashboards should not exist - e2e.pages.ShareDashboardModal.PublicDashboard.WillBePublicCheckbox().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.LimitedDSCheckbox().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.CostIncreaseCheckbox().should('exist'); - e2e.pages.ShareDashboardModal.PublicDashboard.CreateButton().should('exist'); - - e2e.pages.ShareDashboardModal.PublicDashboard.PauseSwitch().should('not.exist'); - }); -}); diff --git a/e2e/dashboards-suite/dashboard-share-externally-create.spec.ts b/e2e/dashboards-suite/dashboard-share-externally-create.spec.ts deleted file mode 100644 index 6548198013d..00000000000 --- a/e2e/dashboards-suite/dashboard-share-externally-create.spec.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { PublicDashboard } from '../../public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils'; -import { e2e } from '../utils'; -import '../utils/support/clipboard'; - -describe('Shared dashboards', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Close share externally drawer', () => { - openDashboard(); - - // Open share externally drawer - e2e.pages.Dashboard.DashNav.newShareButton.arrowMenu().click(); - e2e.pages.Dashboard.DashNav.newShareButton.menu.shareExternally().click(); - - cy.url().should('include', 'shareView=public_dashboard'); - e2e.pages.ShareDashboardDrawer.ShareExternally.container().should('be.visible'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Creation.PublicShare.cancelButton().click(); - - cy.url().should('not.include', 'shareView=public_dashboard'); - e2e.pages.ShareDashboardDrawer.ShareExternally.container().should('not.exist'); - }); - - // Skipping due to being a flaky test - // https://drone.grafana.net/grafana/grafana/201217/6/14 - it.skip('Create a shared dashboard and check API', () => { - openDashboard(); - - // Open share externally drawer - e2e.pages.Dashboard.DashNav.newShareButton.arrowMenu().click(); - e2e.pages.Dashboard.DashNav.newShareButton.menu.shareExternally().click(); - - // Create button should be disabled - e2e.pages.ShareDashboardDrawer.ShareExternally.Creation.PublicShare.createButton().should('be.disabled'); - - // Create flow shouldn't show these elements - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.enableTimeRangeSwitch().should('not.exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.enableAnnotationsSwitch().should('not.exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.copyUrlButton().should('not.exist'); - - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.revokeAccessButton().should('not.exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.toggleAccessButton().should('not.exist'); - - // Acknowledge checkbox - e2e.pages.ShareDashboardDrawer.ShareExternally.Creation.willBePublicCheckbox() - .should('be.enabled') - .click({ force: true }); - - // Create shared dashboard - cy.intercept('POST', '/api/dashboards/uid/edediimbjhdz4b/public-dashboards').as('create'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Creation.PublicShare.createButton().should('be.enabled').click(); - cy.wait('@create') - .its('response.body') - .then((body: PublicDashboard) => { - cy.log(JSON.stringify(body)); - cy.clearCookies() - .request(getPublicDashboardAPIUrl(body.accessToken)) - .then((resp) => { - expect(resp.status).to.eq(200); - }); - }); - - // These elements shouldn't be rendered after creating public dashboard - e2e.pages.ShareDashboardDrawer.ShareExternally.Creation.willBePublicCheckbox().should('not.exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Creation.PublicShare.createButton().should('not.exist'); - - // These elements should be rendered - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.enableTimeRangeSwitch().should('exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.enableAnnotationsSwitch().should('exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.copyUrlButton().should('exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.revokeAccessButton().should('exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.toggleAccessButton().should('exist'); - }); - - // Skipping as clipboard permissions are failing in CI. Public dashboard creation is checked in previous test on purpose - it.skip('Open a shared dashboard', () => { - openDashboard(); - - cy.wrap( - Cypress.automation('remote:debugger:protocol', { - command: 'Browser.grantPermissions', - params: { - permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'], - origin: window.location.origin, - }, - }) - ); - - // Tag indicating a dashboard is public - e2e.pages.Dashboard.DashNav.publicDashboardTag().should('exist'); - - // Open share externally drawer - e2e.pages.Dashboard.DashNav.newShareButton.arrowMenu().click(); - e2e.pages.Dashboard.DashNav.newShareButton.menu.shareExternally().click(); - - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.enableTimeRangeSwitch().should('exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.enableAnnotationsSwitch().should('exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.copyUrlButton().should('exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.revokeAccessButton().should('exist'); - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.toggleAccessButton().should('exist'); - - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.copyUrlButton() - .click() - .then(() => { - cy.copyFromClipboard().then((url) => { - cy.clearCookies() - .request(getPublicDashboardAPIUrl(String(url))) - .then((resp) => { - expect(resp.status).to.eq(200); - }); - }); - }); - }); - - it.skip('Disable a shared dashboard', () => { - openDashboard(); - - //TODO Failing in CI/CD. Fix it - // cy.wrap( - // Cypress.automation('remote:debugger:protocol', { - // command: 'Browser.grantPermissions', - // params: { - // permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'], - // origin: window.location.origin, - // }, - // }) - // ); - - // Open share externally drawer - e2e.pages.Dashboard.DashNav.newShareButton.arrowMenu().click(); - e2e.pages.Dashboard.DashNav.newShareButton.menu.shareExternally().click(); - - // Save public dashboard - cy.intercept('PATCH', '/api/dashboards/uid/edediimbjhdz4b/public-dashboards/*').as('update'); - - // Switch off enabling toggle - e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.toggleAccessButton() - .should('be.enabled') - .click({ force: true }); - - cy.wait('@update') - .its('response') - .then((rs) => { - expect(rs.statusCode).eq(200); - const publicDashboard: PublicDashboard = rs.body; - cy.clearCookies() - .request({ url: getPublicDashboardAPIUrl(publicDashboard.accessToken), failOnStatusCode: false }) - .then((resp) => { - expect(resp.status).to.eq(403); - }); - }); - // .then(() => { - // e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.toggleAccessButton().contains('Resume access'); - // e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.copyUrlButton().should('be.enabled'); - // }); - - //TODO Failing in CI/CD. Fix it - // e2e.pages.ShareDashboardDrawer.ShareExternally.Configuration.copyUrlButton() - // .click() - // .then(() => { - // cy.copyFromClipboard().then((url) => { - // cy.clearCookies() - // .request({ url: getPublicDashboardAPIUrl(String(url)), failOnStatusCode: false }) - // .then((resp) => { - // expect(resp.status).to.eq(403); - // }); - // }); - // }); - }); -}); - -const openDashboard = () => { - e2e.flows.openDashboard({ - uid: 'edediimbjhdz4b', - queryParams: { '__feature.scenes': true, '__feature.newDashboardSharingComponent': true }, - }); -}; - -const getPublicDashboardAPIUrl = (accessToken: string): string => { - return `/api/public/dashboards/${accessToken}`; -}; diff --git a/e2e/dashboards-suite/dashboard-share-internally.spec.ts b/e2e/dashboards-suite/dashboard-share-internally.spec.ts deleted file mode 100644 index 76773fa7f71..00000000000 --- a/e2e/dashboards-suite/dashboard-share-internally.spec.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { ShareLinkConfiguration } from '../../public/app/features/dashboard-scene/sharing/ShareButton/utils'; -import { e2e } from '../utils'; -import '../utils/support/clipboard'; - -describe('Share internally', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - cy.window().then((win) => { - win.localStorage.removeItem('grafana.dashboard.link.shareConfiguration'); - }); - }); - - it('Create a locked time range short link', () => { - cy.intercept({ - pathname: '/api/ds/query', - }).as('query'); - openDashboard(); - cy.wait('@query'); - - //TODO Failing in CI/CD. Fix it - // cy.wrap( - // Cypress.automation('remote:debugger:protocol', { - // command: 'Browser.grantPermissions', - // params: { - // permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'], - // origin: window.location.origin, - // }, - // }) - // ); - - // Open share externally drawer - e2e.pages.Dashboard.DashNav.newShareButton.arrowMenu().click(); - - cy.intercept('POST', '/api/short-urls').as('create'); - e2e.pages.Dashboard.DashNav.newShareButton.menu.shareInternally().click(); - - cy.url().should('include', 'shareView=link'); - - e2e.pages.ShareDashboardDrawer.ShareInternally.lockTimeRangeSwitch().should('exist'); - e2e.pages.ShareDashboardDrawer.ShareInternally.shortenUrlSwitch().should('exist'); - e2e.pages.ShareDashboardDrawer.ShareInternally.copyUrlButton().should('exist'); - e2e.components.RadioButton.container().should('have.length', 3); - - cy.window().then((win) => { - const shareConfiguration = win.localStorage.getItem('grafana.dashboard.link.shareConfiguration'); - expect(shareConfiguration).equal(null); - }); - cy.wait('@create') - .its('response') - .then((rs) => { - expect(rs.statusCode).eq(200); - const body: { url: string; uid: string } = rs.body; - expect(body.url).contain('goto'); - - // const url = fromBaseUrl(getShortLinkUrl(body.uid)); - // cy.intercept('GET', url).as('get'); - // cy.visit(url, { retryOnNetworkFailure: true }); - // cy.wait('@get'); - // - // cy.url().should('not.include', 'from=now-6h&to=now'); - }); - }); - - it('Create a relative time range short link', () => { - cy.intercept({ - pathname: '/api/ds/query', - }).as('query'); - openDashboard(); - cy.wait('@query'); - - e2e.pages.Dashboard.DashNav.newShareButton.arrowMenu().click(); - - e2e.pages.Dashboard.DashNav.newShareButton.menu.shareInternally().click(); - - cy.intercept('POST', '/api/short-urls').as('update'); - e2e.pages.ShareDashboardDrawer.ShareInternally.lockTimeRangeSwitch().click({ force: true }); - - cy.window().then((win) => { - const shareConfiguration = win.localStorage.getItem('grafana.dashboard.link.shareConfiguration'); - const { useAbsoluteTimeRange, useShortUrl, theme }: ShareLinkConfiguration = JSON.parse(shareConfiguration); - expect(useAbsoluteTimeRange).eq(false); - expect(useShortUrl).eq(true); - expect(theme).eq('current'); - }); - - cy.wait('@update') - .its('response') - .then((rs) => { - expect(rs.statusCode).eq(200); - const body: { url: string; uid: string } = rs.body; - expect(body.url).contain('goto'); - - // const url = fromBaseUrl(getShortLinkUrl(body.uid)); - // cy.intercept('GET', url).as('get'); - // cy.visit(url, { retryOnNetworkFailure: true }); - // cy.wait('@get'); - // - // cy.url().should('include', 'from=now-6h&to=now'); - }); - - // - // e2e.pages.ShareDashboardDrawer.ShareInternally.shortenUrlSwitch().click({ force: true }); - // - // cy.window().then((win) => { - // const shareConfiguration = win.localStorage.getItem('grafana.dashboard.link.shareConfiguration'); - // const { useAbsoluteTimeRange, useShortUrl, theme }: ShareLinkConfiguration = JSON.parse(shareConfiguration); - // expect(useAbsoluteTimeRange).eq(true); - // expect(useShortUrl).eq(false); - // expect(theme).eq('current'); - // }); - - // e2e.pages.ShareDashboardDrawer.ShareInternally.copyUrlButton().should('exist'); - - // e2e.pages.ShareDashboardDrawer.ShareInternally.copyUrlButton() - // .click() - // .then(() => { - // cy.copyFromClipboard().then((url) => { - // cy.wrap(url).should('include', 'from=now-6h&to=now'); - // cy.wrap(url).should('not.include', 'goto'); - // }); - // }); - }); - - it('Create a relative time range short link', () => { - cy.intercept({ - pathname: '/api/ds/query', - }).as('query'); - openDashboard(); - cy.wait('@query'); - - e2e.pages.Dashboard.DashNav.newShareButton.arrowMenu().click(); - - e2e.pages.Dashboard.DashNav.newShareButton.menu.shareInternally().click(); - - cy.intercept('POST', '/api/short-urls').as('update'); - e2e.pages.ShareDashboardDrawer.ShareInternally.lockTimeRangeSwitch().click({ force: true }); - - cy.window().then((win) => { - const shareConfiguration = win.localStorage.getItem('grafana.dashboard.link.shareConfiguration'); - const { useAbsoluteTimeRange, useShortUrl, theme }: ShareLinkConfiguration = JSON.parse(shareConfiguration); - expect(useAbsoluteTimeRange).eq(false); - expect(useShortUrl).eq(true); - expect(theme).eq('current'); - }); - - cy.wait('@update') - .its('response') - .then((rs) => { - expect(rs.statusCode).eq(200); - const body: { url: string; uid: string } = rs.body; - expect(body.url).contain('goto'); - - // const url = fromBaseUrl(getShortLinkUrl(body.uid)); - // cy.intercept('GET', url).as('get'); - // cy.visit(url, { retryOnNetworkFailure: true }); - // cy.wait('@get'); - // - // cy.url().should('include', 'from=now-6h&to=now'); - }); - }); - - //TODO Failing in CI/CD. Fix it - it.skip('Share button gets configured link', () => { - cy.intercept({ - pathname: '/api/ds/query', - }).as('query'); - openDashboard(); - cy.wait('@query'); - - // cy.wrap( - // Cypress.automation('remote:debugger:protocol', { - // command: 'Browser.grantPermissions', - // params: { - // permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'], - // origin: window.location.origin, - // }, - // }) - // ); - - //TODO Failing in CI/CD. Fix it - // e2e.pages.Dashboard.DashNav.newShareButton - // .shareLink() - // .click() - // .then(() => { - // cy.window() - // .then((win) => { - // return win.navigator.clipboard.readText().then((url) => { - // cy.wrap(url).as('url'); - // }); - // }) - // .then(() => { - // cy.get('@url').then((url) => { - // cy.wrap(url).should('not.include', 'from=now-6h&to=now'); - // cy.wrap(url).should('include', 'goto'); - // }); - // }); - // }); - - // Open share externally drawer - e2e.pages.Dashboard.DashNav.newShareButton.arrowMenu().click(); - e2e.pages.Dashboard.DashNav.newShareButton.menu.shareInternally().click(); - - cy.window().then((win) => { - const shareConfiguration = win.localStorage.getItem('grafana.dashboard.link.shareConfiguration'); - expect(shareConfiguration).equal(null); - }); - - e2e.pages.ShareDashboardDrawer.ShareInternally.shortenUrlSwitch().click({ force: true }); - e2e.pages.ShareDashboardDrawer.ShareInternally.lockTimeRangeSwitch().click({ force: true }); - - e2e.components.Drawer.General.close().click(); - - cy.url().should('not.include', 'shareView=link'); - - //TODO Failing in CI/CD. Fix it - // e2e.pages.Dashboard.DashNav.newShareButton - // .shareLink() - // .click() - // .then(() => { - // cy.window() - // .then((win) => { - // return win.navigator.clipboard.readText().then((url) => { - // cy.wrap(url).as('url'); - // }); - // }) - // .then(() => { - // cy.get('@url').then((url) => { - // cy.wrap(url).should('include', 'from=now-6h&to=now'); - // cy.wrap(url).should('not.include', 'goto'); - // }); - // }); - // }); - }); -}); - -const openDashboard = () => { - e2e.flows.openDashboard({ - uid: 'ZqZnVvFZz', - queryParams: { '__feature.scenes': true, '__feature.newDashboardSharingComponent': true }, - timeRange: { from: 'now-6h', to: 'now' }, - }); -}; - -// const getShortLinkUrl = (uid: string): string => { -// return `/goto/${uid}`; -// }; diff --git a/e2e/dashboards-suite/dashboard-share-snapshot-create.spec.ts b/e2e/dashboards-suite/dashboard-share-snapshot-create.spec.ts deleted file mode 100644 index 6661750718e..00000000000 --- a/e2e/dashboards-suite/dashboard-share-snapshot-create.spec.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { SnapshotCreateResponse } from '../../public/app/features/dashboard/services/SnapshotSrv'; -import { e2e } from '../utils'; -import { fromBaseUrl } from '../utils/support/url'; -import '../utils/support/clipboard'; - -describe('Snapshots', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Create a snapshot dashboard', () => { - // Opening a dashboard - cy.intercept({ - pathname: '/api/ds/query', - }).as('query'); - e2e.flows.openDashboard({ - uid: 'ZqZnVvFZz', - queryParams: { '__feature.scenes': true, '__feature.newDashboardSharingComponent': true }, - }); - cy.wait('@query'); - - //TODO Failing in CI/CD. Fix it - // cy.wrap( - // Cypress.automation('remote:debugger:protocol', { - // command: 'Browser.grantPermissions', - // params: { - // permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'], - // origin: window.location.origin, - // }, - // }) - // ); - - const panelsToCheck = [ - 'Raw Data Graph', - 'Last non-null', - 'min', - 'Max', - 'The data from graph above with seriesToColumns transform', - ]; - - // Open the sharing drawer - e2e.pages.Dashboard.DashNav.newShareButton.arrowMenu().click(); - e2e.pages.Dashboard.DashNav.newShareButton.menu.shareSnapshot().click(); - - // Publish snapshot - cy.intercept('POST', '/api/snapshots').as('create'); - e2e.pages.ShareDashboardDrawer.ShareSnapshot.publishSnapshot().click(); - cy.wait('@create') - .its('response') - .then((rs) => { - expect(rs.statusCode).eq(200); - const body: SnapshotCreateResponse = rs.body; - cy.visit(fromBaseUrl(getSnapshotUrl(body.key))); - - // Validate the dashboard controls are rendered - e2e.pages.Dashboard.Controls().should('exist'); - - // Validate the panels are rendered - for (const title of panelsToCheck) { - e2e.components.Panels.Panel.title(title).should('be.visible'); - } - }); - - // Copy link button should be visible - // e2e.pages.ShareDashboardDrawer.ShareSnapshot.copyUrlButton().should('exist'); - - //TODO Failing in CI/CD. Fix it - // Copy the snapshot URL form the clipboard and open the snapshot - // e2e.pages.ShareDashboardDrawer.ShareSnapshot.copyUrlButton() - // .click() - // .then(() => { - // cy.copyFromClipboard().then((url) => { - // cy.wrap(url).as('url'); - // }); - // }) - // .then(() => { - // cy.get('@url').then((url) => { - // e2e.pages.ShareDashboardDrawer.ShareSnapshot.visit(getSnapshotKey(String(url))); - // }); - // - // // Validate the dashboard controls are rendered - // e2e.pages.Dashboard.Controls().should('exist'); - // - // // Validate the panels are rendered - // for (const title of panelsToCheck) { - // e2e.components.Panels.Panel.title(title).should('be.visible'); - // } - // }); - }); -}); - -const getSnapshotUrl = (uid: string): string => { - return `/dashboard/snapshot/${uid}`; -}; - -// const getSnapshotKey = (url: string): string => { -// return url.split('/').pop(); -// }; diff --git a/e2e/dashboards-suite/dashboard-templating.spec.ts b/e2e/dashboards-suite/dashboard-templating.spec.ts deleted file mode 100644 index 0449f408868..00000000000 --- a/e2e/dashboards-suite/dashboard-templating.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { e2e } from '../utils'; - -describe('Dashboard templating', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - // Note: Only works in Chrome/Chromium-based browsers - Cypress.automation('remote:debugger:protocol', { - command: 'Emulation.setTimezoneOverride', - params: { - timezoneId: 'Pacific/Easter', // OR 'UTC' - }, - }); - }); - - it('Verify variable interpolation works', () => { - // Open dashboard global variables and interpolation - e2e.flows.openDashboard({ uid: 'HYaGDGIMk' }); - - const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; - const example = `Example: from=now-6h&to=now&timezone=${encodeURIComponent(timeZone)}`; - - const items: string[] = []; - const expectedItems: string[] = [ - '__dashboard = Templating - Global variables and interpolation', - '__dashboard.name = Templating - Global variables and interpolation', - '__dashboard.uid = HYaGDGIMk', - '__org.name = Main Org.', - '__org.id = 1', - '__user.id = 1', - '__user.login = admin', - '__user.email = admin@localhost', - `Server:raw = A'A"A,BB\\B,CCC`, - `Server:regex = (A'A"A|BB\\\\B|CCC)`, - `Server:lucene = ("A'A\\"A" OR "BB\\\\B" OR "CCC")`, - `Server:glob = {A'A"A,BB\\B,CCC}`, - `Server:pipe = A'A"A|BB\\B|CCC`, - `Server:distributed = A'A"A,Server=BB\\B,Server=CCC`, - `Server:csv = A'A"A,BB\\B,CCC`, - `Server:html = A'A"A, BB\\B, CCC`, - `Server:json = ["A'A\\"A","BB\\\\B","CCC"]`, - `Server:percentencode = %7BA%27A%22A%2CBB%5CB%2CCCC%7D`, - `Server:singlequote = 'A\\'A"A','BB\\B','CCC'`, - `Server:doublequote = "A'A\\"A","BB\\B","CCC"`, - `Server:sqlstring = 'A''A\\"A','BB\\\B','CCC'`, - `Server:date = NaN`, - `Server:text = All`, - `Server:queryparam = var-Server=$__all`, - `1 < 2`, - example, - ]; - - cy.get('.markdown-html li') - .should('have.length', 26) - .each((element) => { - items.push(element.text()); - }) - .then(() => { - expectedItems.forEach((expected, index) => { - expect(items[index]).to.equal(expected); - }); - }); - - // Check link interpolation is working correctly - cy.contains('a', example).should( - 'have.attr', - 'href', - `https://example.com/?from=now-6h&to=now&timezone=${encodeURIComponent(timeZone)}` - ); - }); -}); diff --git a/e2e/dashboards-suite/dashboard-time-zone.spec.ts b/e2e/dashboards-suite/dashboard-time-zone.spec.ts deleted file mode 100644 index 0361e84a236..00000000000 --- a/e2e/dashboards-suite/dashboard-time-zone.spec.ts +++ /dev/null @@ -1,263 +0,0 @@ -import { - addDays, - addHours, - differenceInCalendarDays, - differenceInMinutes, - format, - isBefore, - parseISO, - toDate, -} from 'date-fns'; - -import { e2e } from '../utils'; - -describe('Dashboard time zone support', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it.skip('Tests dashboard time zone scenarios', () => { - e2e.flows.openDashboard({ uid: '5SdHCasdf' }); - - const fromTimeZone = 'UTC'; - const toTimeZone = 'America/Chicago'; - const offset = offsetBetweenTimeZones(toTimeZone, fromTimeZone); - - const panelsToCheck = [ - 'Random walk series', - 'Millisecond res x-axis and tooltip', - '2 yaxis and axis labels', - 'Stacking value ontop of nulls', - 'Null between points', - 'Legend Table No Scroll Visible', - ]; - - const timesInUtc: Record = {}; - - for (const title of panelsToCheck) { - e2e.components.Panels.Panel.title(title) - .should('be.visible') - .within(() => { - e2e.components.Panels.Visualization.Graph.xAxis.labels().should('be.visible'); - e2e.components.Panels.Visualization.Graph.xAxis - .labels() - .last() - .should((element) => { - timesInUtc[title] = element.text(); - }); - }); - } - - e2e.components.PageToolbar.item('Dashboard settings').click(); - - e2e.components.TimeZonePicker.containerV2() - .should('be.visible') - .within(() => { - e2e.components.Select.singleValue().should('have.text', 'Coordinated Universal Time'); - e2e.components.Select.input().should('be.visible').click(); - }); - - e2e.components.Select.option().should('be.visible').contains(toTimeZone).click(); - - // click to go back to the dashboard. - e2e.pages.Dashboard.Settings.Actions.close().click(); - e2e.components.RefreshPicker.runButtonV2().should('be.visible').click(); - - for (const title of panelsToCheck) { - e2e.components.Panels.Panel.title(title) - .should('be.visible') - .within(() => { - e2e.components.Panels.Visualization.Graph.xAxis.labels().should('be.visible'); - e2e.components.Panels.Visualization.Graph.xAxis - .labels() - .last() - .should((element) => { - const inUtc = timesInUtc[title]; - const inTz = element.text(); - const isCorrect = isTimeCorrect(inUtc, inTz, offset); - expect(isCorrect).to.be.equal(true); - }); - }); - } - }); - - // TODO: remove skip once https://github.com/grafana/grafana/issues/86420 is done - it.skip('Tests relative timezone support and overrides', () => { - // Open dashboard - e2e.flows.openDashboard({ - uid: 'd41dbaa2-a39e-4536-ab2b-caca52f1a9c8', - }); - - cy.intercept('/api/ds/query*').as('dataQuery'); - - // Switch to Browser timezone - e2e.flows.setTimeRange({ - from: 'now-6h', - to: 'now', - zone: 'Browser', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Today so far, still in Browser timezone - e2e.flows.setTimeRange({ - from: 'now/d', - to: 'now', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - e2e.components.Panels.Panel.title('Panel in timezone') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - // Test UTC timezone - e2e.flows.setTimeRange({ - from: 'now-6h', - to: 'now', - zone: 'Coordinated Universal Time', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Today so far, still in UTC timezone - e2e.flows.setTimeRange({ - from: 'now/d', - to: 'now', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - e2e.components.Panels.Panel.title('Panel in timezone') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Test Tokyo timezone - e2e.flows.setTimeRange({ - from: 'now-6h', - to: 'now', - zone: 'Asia/Tokyo', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Today so far, still in Tokyo timezone - e2e.flows.setTimeRange({ - from: 'now/d', - to: 'now', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - e2e.components.Panels.Panel.title('Panel in timezone') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Test LA timezone - e2e.flows.setTimeRange({ - from: 'now-6h', - to: 'now', - zone: 'America/Los_Angeles', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - // Today so far, still in LA timezone - e2e.flows.setTimeRange({ - from: 'now/d', - to: 'now', - }); - // Need to wait for 2 calls as there's 2 panels - cy.wait(['@dataQuery', '@dataQuery']); - - e2e.components.Panels.Panel.title('Panel with relative time override') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - - e2e.components.Panels.Panel.title('Panel in timezone') - .should('be.visible') - .within(() => { - cy.contains('[role="row"]', '00:00:00').should('be.visible'); - }); - }); -}); - -const isTimeCorrect = (inUtc: string, inTz: string, offset: number): boolean => { - if (inUtc === inTz) { - // we need to catch issues when timezone isn't changed for some reason like https://github.com/grafana/grafana/issues/35504 - return false; - } - - const reference = format(new Date(), 'yyyy-LL-dd'); - - const utcDate = toDate(parseISO(`${reference} ${inUtc}`)); - const utcDateWithOffset = addHours(toDate(parseISO(`${reference} ${inUtc}`)), offset); - const dayDifference = differenceInCalendarDays(utcDate, utcDateWithOffset); // if the utcDate +/- offset is the day before/after then we need to adjust reference - const dayOffset = isBefore(utcDateWithOffset, utcDate) ? dayDifference * -1 : dayDifference; - const tzDate = addDays(toDate(parseISO(`${reference} ${inTz}`)), dayOffset); // adjust tzDate with any dayOffset - const diff = Math.abs(differenceInMinutes(utcDate, tzDate)); // use Math.abs if tzDate is in future - - return diff <= Math.abs(offset * 60); -}; - -const offsetBetweenTimeZones = (timeZone1: string, timeZone2: string, when: Date = new Date()): number => { - const t1 = convertDateToAnotherTimeZone(when, timeZone1); - const t2 = convertDateToAnotherTimeZone(when, timeZone2); - return (t1.getTime() - t2.getTime()) / (1000 * 60 * 60); -}; - -const convertDateToAnotherTimeZone = (date: Date, timeZone: string): Date => { - const dateString = date.toLocaleString('en-US', { - timeZone: timeZone, - }); - return new Date(dateString); -}; diff --git a/e2e/dashboards-suite/dashboard-timepicker.spec.ts b/e2e/dashboards-suite/dashboard-timepicker.spec.ts deleted file mode 100644 index e4ffbec6299..00000000000 --- a/e2e/dashboards-suite/dashboard-timepicker.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { e2e } from '../utils'; - -describe('Dashboard timepicker', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Shows the correct calendar days with custom timezone set via preferences', () => { - e2e.flows.setUserPreferences({ - timezone: 'Asia/Tokyo', - }); - - // Open dashboard with time range from 8th to end of 10th. - // Will be Tokyo time because of above preference - e2e.flows.openDashboard({ - uid: '5SdHCasdf', - timeRange: { - zone: 'Default', - from: '2022-06-08 00:00:00', - to: '2022-06-10 23:59:59', - }, - }); - - // Assert that the calendar shows 08 and 09 and 10 as selected days - e2e.components.TimePicker.openButton().click(); - e2e.components.TimePicker.calendar.openButton().first().click(); - cy.get('.react-calendar__tile--active, .react-calendar__tile--hasActive').should('have.length', 3); - }); - - it('Shows the correct calendar days with custom timezone set via time picker', () => { - // Open dashboard with time range from 2022-06-08 00:00:00 to 2022-06-10 23:59:59 in Tokyo time - e2e.flows.openDashboard({ - uid: '5SdHCasdf', - timeRange: { - zone: 'Asia/Tokyo', - from: '2022-06-08 00:00:00', - to: '2022-06-10 23:59:59', - }, - }); - - // Assert that the calendar shows 08 and 09 and 10 as selected days - e2e.components.TimePicker.openButton().click(); - e2e.components.TimePicker.calendar.openButton().first().click(); - cy.get('.react-calendar__tile--active, .react-calendar__tile--hasActive').should('have.length', 3); - }); -}); diff --git a/e2e/dashboards-suite/embedded-dashboard.spec.ts b/e2e/dashboards-suite/embedded-dashboard.spec.ts deleted file mode 100644 index 8c52271994c..00000000000 --- a/e2e/dashboards-suite/embedded-dashboard.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { selectors } from '@grafana/e2e-selectors'; - -import { e2e } from '../utils'; -import { fromBaseUrl } from '../utils/support/url'; - -describe('Embedded dashboard', function () { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('open test page', function () { - cy.visit(fromBaseUrl('/dashboards/embedding-test')); - - // Verify pie charts are rendered - cy.get( - `[data-viz-panel-key="panel-11"] [data-testid^="${selectors.components.Panels.Visualization.PieChart.svgSlice}"]` - ).should('have.length', 5); - - // Verify no url sync - e2e.components.TimePicker.openButton().click(); - cy.get('label:contains("Last 1 hour")').click(); - cy.url().should('eq', fromBaseUrl('/dashboards/embedding-test')); - }); -}); diff --git a/e2e/dashboards-suite/general-dashboards.spec.ts b/e2e/dashboards-suite/general-dashboards.spec.ts deleted file mode 100644 index bc1b2a306be..00000000000 --- a/e2e/dashboards-suite/general-dashboards.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { e2e } from '../utils'; - -const PAGE_UNDER_TEST = 'edediimbjhdz4b/a-tall-dashboard'; - -describe('Dashboards', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('should restore scroll position', () => { - e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST }); - e2e.components.Panels.Panel.title('Panel #1').should('be.visible'); - - // scroll to the bottom - cy.scrollTo('bottom', { - timeout: 5 * 1000, - }); - - // The last panel should be visible... - e2e.components.Panels.Panel.title('Panel #50').should('be.visible'); - - // Then we open and close the panel editor - e2e.components.Panels.Panel.menu('Panel #50').click({ force: true }); // it only shows on hover - e2e.components.Panels.Panel.menuItems('Edit').click(); - e2e.components.NavToolbar.editDashboard.backToDashboardButton().click(); - - // The last panel should still be visible! - e2e.components.Panels.Panel.title('Panel #50').should('be.visible'); - }); -}); diff --git a/e2e/dashboards-suite/import-dashboard.spec.ts b/e2e/dashboards-suite/import-dashboard.spec.ts deleted file mode 100644 index 6d4bfed5e68..00000000000 --- a/e2e/dashboards-suite/import-dashboard.spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -import testDashboard from '../dashboards/TestDashboard.json'; -import { e2e } from '../utils'; - -describe('Import Dashboards Test', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Ensure you can import a number of json test dashboards from a specific test directory', () => { - e2e.flows.importDashboard(testDashboard, 1000); - }); -}); diff --git a/e2e/dashboards-suite/load-options-from-url.spec.ts b/e2e/dashboards-suite/load-options-from-url.spec.ts deleted file mode 100644 index d674ae2a385..00000000000 --- a/e2e/dashboards-suite/load-options-from-url.spec.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { e2e } from '../utils'; - -const PAGE_UNDER_TEST = '-Y-tnEDWk/templating-nested-template-variables'; - -describe('Variables - Load options from Url', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('default options should be correct', () => { - e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST }); - cy.intercept({ - method: 'POST', - pathname: '/api/ds/query*', - }).as('query'); - - cy.wait('@query'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().parent().should('have.length', 10); - - e2e.components.Select.toggleAllOptions().should('have.text', 'Selected (1)'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('C').should('be.visible'); - - cy.get('body').click(0, 0); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('AA') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().parent().should('have.length', 10); - - e2e.components.Select.toggleAllOptions().should('have.text', 'Selected (1)'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AA').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AB').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AC').should('be.visible'); - - cy.get('body').click(0, 0); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('$__all') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().parent().should('have.length', 10); - - e2e.components.Select.toggleAllOptions().should('have.text', 'Selected (1)'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAA').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAB').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAC').should('be.visible'); - }); - - it('options set in url should load correct options', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&var-datacenter=B&var-server=BB&var-pod=BBB` }); - cy.intercept({ - method: 'POST', - pathname: '/api/ds/query', - }).as('query'); - - cy.wait('@query'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('B') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().parent().should('have.length', 10); - - e2e.components.Select.toggleAllOptions().should('have.text', 'Selected (1)'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('C').should('be.visible'); - - cy.get('body').click(0, 0); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('BB') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().parent().should('have.length', 10); - - e2e.components.Select.toggleAllOptions().should('have.text', 'Selected (1)'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BA').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BB').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BC').should('be.visible'); - - cy.get('body').click(0, 0); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('BBB') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().parent().should('have.length', 10); - - e2e.components.Select.toggleAllOptions().should('have.text', 'Selected (1)'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBA').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBB').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBC').should('be.visible'); - }); - - it('options set in url that do not exist should load correct options', () => { - // @ts-ignore some typing issue - cy.on('uncaught:exception', (err) => { - if (err.stack?.indexOf("Couldn't find any field of type string in the results.") !== -1) { - // return false to prevent the error from - // failing this test - return false; - } - - return true; - }); - - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&var-datacenter=X` }); - cy.intercept({ - method: 'POST', - pathname: '/api/ds/query', - }).as('query'); - - cy.wait('@query'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('X') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().parent().should('have.length', 10); - - e2e.components.Select.toggleAllOptions().should('have.text', 'Selected (1)'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('C').should('be.visible'); - - cy.get('body').click(); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('$__all') - .should('be.visible') - .should('have.length', 2); - }); -}); diff --git a/e2e/dashboards-suite/new-constant-variable.spec.ts b/e2e/dashboards-suite/new-constant-variable.spec.ts deleted file mode 100644 index 42693acb668..00000000000 --- a/e2e/dashboards-suite/new-constant-variable.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { e2e } from '../utils'; - -const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output'; -const DASHBOARD_NAME = 'Test variable output'; - -describe('Variables - Constant', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('can add a new constant variable', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&editview=variables` }); - cy.contains(DASHBOARD_NAME).should('be.visible'); - - // Create a new "Constant" variable - e2e.components.CallToActionCard.buttonV2('Add variable').click(); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => { - cy.get('input').type('Constant{enter}'); - }); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type('VariableUnderTest').blur(); - e2e.pages.Dashboard.Settings.Variables.Edit.ConstantVariable.constantOptionsQueryInputV2().type('pesto').blur(); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type('Variable under test').blur(); - - // e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().eq(0).should('have.text', 'pesto'); - - // Navigate back to the homepage and change the selected variable value - e2e.pages.Dashboard.Settings.Variables.Edit.General.applyButton().click(); - e2e.components.NavToolbar.editDashboard.backToDashboardButton().click(); - e2e.components.RefreshPicker.runButtonV2().click(); - - // Assert it was rendered - cy.get('.markdown-html').should('include.text', 'VariableUnderTest: pesto'); - - // Assert the variable is not visible in the dashboard nav - e2e.pages.Dashboard.SubMenu.submenuItemLabels('Variable under test').should('not.exist'); - }); -}); diff --git a/e2e/dashboards-suite/new-custom-variable.spec.ts b/e2e/dashboards-suite/new-custom-variable.spec.ts deleted file mode 100644 index bda748fbdf2..00000000000 --- a/e2e/dashboards-suite/new-custom-variable.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { e2e } from '../utils'; - -const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output'; -const DASHBOARD_NAME = 'Test variable output'; - -function fillInCustomVariable(name: string, label: string, value: string) { - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => { - cy.get('input').type('Custom{enter}'); - }); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type(name).blur(); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type(label).blur(); - e2e.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput().type(value).blur(); -} - -function assertPreviewValues(expectedValues: string[]) { - for (const expected of expectedValues) { - const index = expectedValues.indexOf(expected); - e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().eq(index).should('have.text', expected); - } -} - -describe('Variables - Custom', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('can add a custom template variable', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&editview=variables` }); - cy.contains(DASHBOARD_NAME).should('be.visible'); - - // Create a new "Custom" variable - e2e.components.CallToActionCard.buttonV2('Add variable').click(); - fillInCustomVariable('VariableUnderTest', 'Variable under test', 'one,two,three'); - assertPreviewValues(['one', 'two', 'three']); - - // Navigate back to the homepage and change the selected variable value - e2e.pages.Dashboard.Settings.Variables.Edit.General.applyButton().click(); - e2e.components.NavToolbar.editDashboard.backToDashboardButton().click(); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('one').click(); - e2e.components.Select.option().contains('two').click(); - // Assert it was rendered - cy.get('.markdown-html').should('include.text', 'VariableUnderTest: two'); - }); - - it('can add a custom template variable with labels', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&editview=variables` }); - cy.contains(DASHBOARD_NAME).should('be.visible'); - - // Create a new "Custom" variable - e2e.components.CallToActionCard.buttonV2('Add variable').click(); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => { - cy.get('input').type('Custom{enter}'); - }); - - // Set its name, label, and content - fillInCustomVariable('VariableUnderTest', 'Variable under test', 'One : 1,Two : 2, Three : 3'); - assertPreviewValues(['One', 'Two', 'Three']); - - // Navigate back to the homepage and change the selected variable value - e2e.pages.Dashboard.Settings.Variables.Edit.General.applyButton().click(); - e2e.components.NavToolbar.editDashboard.backToDashboardButton().click(); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('1').click(); - e2e.components.Select.option().contains('Two').click(); - - // Assert it was rendered - cy.get('.markdown-html').should('include.text', 'VariableUnderTest: 2'); - }); -}); diff --git a/e2e/dashboards-suite/new-datasource-variable.spec.ts b/e2e/dashboards-suite/new-datasource-variable.spec.ts deleted file mode 100644 index b56e458567c..00000000000 --- a/e2e/dashboards-suite/new-datasource-variable.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { e2e } from '../utils'; - -const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output'; -const DASHBOARD_NAME = 'Test variable output'; - -// Skipping due to flakiness/race conditions with same old arch test e2e/dashboards-suite/new-datasource-variable.spec.ts -describe.skip('Variables - Datasource', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('can add a new datasource variable', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&editview=variables` }); - cy.contains(DASHBOARD_NAME).should('be.visible'); - - // Create a new "Datasource" variable - e2e.components.CallToActionCard.buttonV2('Add variable').click(); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => { - cy.get('input').type('Data source{enter}'); - }); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type('VariableUnderTest').blur(); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type('Variable under test').blur(); - - // If this is failing, but sure to check there are Prometheus datasources named "gdev-prometheus" and "gdev-slow-prometheus" - // Or, just update is to match some gdev datasources to test with :) - e2e.pages.Dashboard.Settings.Variables.Edit.DatasourceVariable.datasourceSelect().within(() => { - cy.get('input').type('Prometheus{enter}'); - }); - e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should( - 'contain.text', - 'gdev-prometheus' - ); - e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should( - 'contain.text', - 'gdev-slow-prometheus' - ); - - // Navigate back to the homepage and change the selected variable value - e2e.pages.Dashboard.Settings.Variables.Edit.General.applyButton().click(); - e2e.components.NavToolbar.editDashboard.backToDashboardButton().click(); - e2e.components.RefreshPicker.runButtonV2().click(); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('gdev-prometheus').click(); - e2e.components.Select.option().contains('gdev-slow-prometheus').click(); - - // Assert it was rendered - cy.get('.markdown-html').should('include.text', 'VariableUnderTest: gdev-slow-prometheus-uid'); - cy.get('.markdown-html').should('include.text', 'VariableUnderTestText: gdev-slow-prometheus'); - }); -}); diff --git a/e2e/dashboards-suite/new-interval-variable.spec.ts b/e2e/dashboards-suite/new-interval-variable.spec.ts deleted file mode 100644 index 88e1052ce5e..00000000000 --- a/e2e/dashboards-suite/new-interval-variable.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { e2e } from '../utils'; - -const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output'; -const DASHBOARD_NAME = 'Test variable output'; - -function assertPreviewValues(expectedValues: string[]) { - for (const expected of expectedValues) { - const index = expectedValues.indexOf(expected); - e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().eq(index).should('have.text', expected); - } -} - -describe('Variables - Interval', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('can add a new interval variable', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&editview=variables` }); - cy.contains(DASHBOARD_NAME).should('be.visible'); - - // Create a new "Interval" variable - e2e.components.CallToActionCard.buttonV2('Add variable').click(); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => { - cy.get('input').type('Interval{enter}'); - }); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type('VariableUnderTest').blur(); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type('Variable under test').blur(); - - e2e.pages.Dashboard.Settings.Variables.Edit.IntervalVariable.intervalsValueInput() - .clear() - .type('10s,10m,60m,90m,1h30m') - .blur(); - - assertPreviewValues(['10s', '10m', '60m', '90m', '1h30m']); - - // Navigate back to the homepage and change the selected variable value - e2e.pages.Dashboard.Settings.Variables.Edit.General.applyButton().click(); - e2e.components.NavToolbar.editDashboard.backToDashboardButton().click(); - - e2e.components.RefreshPicker.runButtonV2().click(); - - e2e.pages.Dashboard.SubMenu.submenuItemLabels('Variable under test').next().should('have.text', `10s`).click(); - e2e.components.Select.option().contains('1h30m').click(); - - // Assert it was rendered - cy.get('.markdown-html').should('include.text', 'VariableUnderTest: 1h30m'); - }); -}); diff --git a/e2e/dashboards-suite/new-query-variable.spec.ts b/e2e/dashboards-suite/new-query-variable.spec.ts deleted file mode 100644 index 4b3c09a6eb0..00000000000 --- a/e2e/dashboards-suite/new-query-variable.spec.ts +++ /dev/null @@ -1,184 +0,0 @@ -import { selectors } from '@grafana/e2e-selectors'; - -import { e2e } from '../utils'; - -const PAGE_UNDER_TEST = '-Y-tnEDWk/templating-nested-template-variables'; -const DASHBOARD_NAME = 'Templating - Nested Template Variables'; - -describe('Variables - Query - Add variable', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('query variable should be default and default fields should be correct', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&editview=variables` }); - cy.contains(DASHBOARD_NAME).should('be.visible'); - - cy.get(`[data-testid="${selectors.pages.Dashboard.Settings.Variables.List.newButton}"]`) - .should('be.visible') - .click(); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2() - .should('be.visible') - .within((input) => { - expect(input.attr('placeholder')).equals('Variable name'); - expect(input.val()).equals('query0'); - }); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2() - .should('be.visible') - .within((select) => { - e2e.components.Select.singleValue().should('have.text', 'Query'); - }); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2() - .should('be.visible') - .within((input) => { - expect(input.attr('placeholder')).equals('Label name'); - expect(input.val()).equals(''); - }); - cy.get('[placeholder="Descriptive text"]') - .should('be.visible') - .within((input) => { - expect(input.attr('placeholder')).equals('Descriptive text'); - expect(input.val()).equals(''); - }); - cy.get('label').contains('Hide').should('be.visible'); - - e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsDataSourceSelect() - .get('input[placeholder="gdev-testdata"]') - .scrollIntoView() - .should('be.visible'); - - cy.get('label').contains('Refresh').scrollIntoView().should('be.visible'); - cy.get('label').contains('On dashboard load').scrollIntoView().should('be.visible'); - - e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2() - .should('be.visible') - .within((input) => { - const placeholder = '/.*-(?.*)-(?.*)-.*/'; - expect(input.attr('placeholder')).equals(placeholder); - expect(input.val()).equals(''); - }); - e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsSortSelectV2() - .should('be.visible') - .within((select) => { - e2e.components.Select.singleValue().should('have.text', 'Disabled'); - }); - - cy.contains('label', 'Multi-value').within(() => { - cy.get('input[type="checkbox"]').should('not.be.checked'); - }); - - cy.contains('label', 'Include All option').within(() => { - cy.get('input[type="checkbox"]').should('not.be.checked'); - }); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should('not.exist'); - e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput().should('not.exist'); - }); - - it('adding a single value query variable', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&editview=variables` }); - cy.contains(DASHBOARD_NAME).should('be.visible'); - - cy.get(`[data-testid="${selectors.pages.Dashboard.Settings.Variables.List.newButton}"]`) - .should('be.visible') - .click(); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2() - .should('be.visible') - .clear() - .type('a label'); - - cy.get('[placeholder="Descriptive text"]').should('be.visible').clear().type('a description'); - - e2e.components.DataSourcePicker.container().should('be.visible').type('gdev-testdata{enter}'); - - e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput() - .should('be.visible') - .type('*') - .blur(); - - e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2() - .should('be.visible') - .type('/.*C.*/') - .blur(); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should('exist'); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.submitButton().scrollIntoView().should('be.visible').click(); - - e2e.components.NavToolbar.editDashboard.backToDashboardButton().click(); - - e2e.pages.Dashboard.SubMenu.submenuItemLabels('a label').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItem() - .should('have.length', 4) - .eq(3) - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().should('have.length', 1); - e2e.components.Select.option().contains('C'); - }); - - it('adding a multi value query variable', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&editview=variables` }); - cy.contains(DASHBOARD_NAME).should('be.visible'); - - cy.get(`[data-testid="${selectors.pages.Dashboard.Settings.Variables.List.newButton}"]`) - .should('be.visible') - .click(); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2() - .should('be.visible') - .clear() - .type('a label'); - - cy.get('[placeholder="Descriptive text"]').should('be.visible').clear().type('a description'); - - e2e.components.DataSourcePicker.container().type('gdev-testdata{enter}'); - - e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsQueryInput() - .should('be.visible') - .type('*') - .blur(); - - e2e.pages.Dashboard.Settings.Variables.Edit.QueryVariable.queryOptionsRegExInputV2() - .should('be.visible') - .type('/.*C.*/') - .blur(); - - cy.contains('label', 'Multi-value').within(() => { - cy.get('input[type="checkbox"]').click({ force: true }).should('be.checked'); - }); - - cy.contains('label', 'Include All option').within(() => { - cy.get('input[type="checkbox"]').click({ force: true }).should('be.checked'); - }); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.selectionOptionsCustomAllInput().within((input) => { - expect(input.val()).equals(''); - }); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption().should('exist'); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.submitButton().scrollIntoView().should('be.visible').click(); - - e2e.components.NavToolbar.editDashboard.backToDashboardButton().click(); - - e2e.pages.Dashboard.SubMenu.submenuItemLabels('a label').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItem() - .should('have.length', 4) - .eq(3) - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().should('have.length', 3); - - e2e.components.Select.toggleAllOptions().should('have.text', 'Selected (1)'); - - e2e.components.Select.option().contains('All'); - e2e.components.Select.option().contains('C'); - }); -}); diff --git a/e2e/dashboards-suite/new-text-box-variable.spec.ts b/e2e/dashboards-suite/new-text-box-variable.spec.ts deleted file mode 100644 index 7da9afa32bf..00000000000 --- a/e2e/dashboards-suite/new-text-box-variable.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { e2e } from '../utils'; - -const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output'; -const DASHBOARD_NAME = 'Test variable output'; - -describe('Variables - Text box', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('can add a new text box variable', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&editview=variables` }); - cy.contains(DASHBOARD_NAME).should('be.visible'); - - // Create a new "text box" variable - e2e.components.CallToActionCard.buttonV2('Add variable').click(); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2().within(() => { - cy.get('input').type('Textbox{enter}'); - }); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2().clear().type('VariableUnderTest').blur(); - e2e.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2().type('Variable under test').blur(); - e2e.pages.Dashboard.Settings.Variables.Edit.TextBoxVariable.textBoxOptionsQueryInputV2().type('cat-dog').blur(); - - // Navigate back to the homepage and change the selected variable value - e2e.pages.Dashboard.Settings.Variables.Edit.General.applyButton().click(); - e2e.components.NavToolbar.editDashboard.backToDashboardButton().click(); - e2e.pages.Dashboard.SubMenu.submenuItem().within(() => { - cy.get('input').clear().type('dog-cat').blur(); - }); - // Assert it was rendered - cy.get('.markdown-html').should('include.text', 'VariableUnderTest: dog-cat'); - }); -}); diff --git a/e2e/dashboards-suite/set-options-from-ui.spec.ts b/e2e/dashboards-suite/set-options-from-ui.spec.ts deleted file mode 100644 index bdcc5c6b516..00000000000 --- a/e2e/dashboards-suite/set-options-from-ui.spec.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { selectors } from '@grafana/e2e-selectors'; - -import { e2e } from '../utils'; - -const PAGE_UNDER_TEST = '-Y-tnEDWk/templating-nested-template-variables'; - -describe('Variables - Set options from ui', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('clicking a value that is not part of dependents options should change these to All', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&var-datacenter=A&var-server=AA&var-pod=AAA` }); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible').click(); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible').click(); - - cy.get('body').click(); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('B').scrollIntoView().should('be.visible'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('$__all') - .should('have.length', 2) - .eq(0) - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().parent().should('have.length', 10); - - e2e.components.Select.toggleAllOptions().should('have.text', 'Selected (1)'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BA').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BB').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BC').should('be.visible'); - - cy.get('body').click(); - - e2e.pages.Dashboard.SubMenu.submenuItemLabels('pod') - .parent() - .within(() => { - cy.get('input').click(); - }); - - // length is 11 because of virtualized select options - e2e.components.Select.option().parent().should('have.length', 11); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAA').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAB').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAC').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAD').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAE').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BAF').should('be.visible'); - }); - - it('adding a value that is not part of dependents options should add the new values dependant options', () => { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1&var-datacenter=A&var-server=AA&var-pod=AAA` }); - cy.intercept({ - pathname: '/api/ds/query', - }).as('query'); - - cy.wait('@query'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('B').should('be.visible').click(); - - e2e.components.Select.toggleAllOptions().should('have.text', 'Selected (2)'); - - cy.get('body').click(); - - cy.wait('@query'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A,B').scrollIntoView().should('be.visible'); - - cy.get(`[aria-label="${selectors.components.LoadingIndicator.icon}"]`).should('not.exist'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('AA') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().should('have.length', 11); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AA').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AB').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AC').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AD').should('be.visible'); - - cy.get('body').click(); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('AAA') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().should('have.length', 10); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAA').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAB').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('AAC').should('be.visible'); - }); - - it('removing a value that is part of dependents options should remove the new values dependant options', () => { - e2e.flows.openDashboard({ - uid: `${PAGE_UNDER_TEST}?orgId=1&var-datacenter=A&var-datacenter=B&var-server=AA&var-server=BB&var-pod=AAA&var-pod=BBB`, - }); - cy.intercept({ pathname: '/api/ds/query' }).as('query'); - - cy.wait('@query'); - cy.get(`[aria-label="${selectors.components.LoadingIndicator.icon}"]`).should('not.exist'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('A,B') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('A').should('be.visible').click(); - - cy.get('body').click(); - - cy.wait('@query'); - cy.get(`[aria-label="${selectors.components.LoadingIndicator.icon}"]`).should('not.exist'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('B').should('be.visible'); - - cy.get(`[aria-label="${selectors.components.LoadingIndicator.icon}"]`).should('not.exist'); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('BB') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.components.Select.option().should('have.length', 10); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('All').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BA').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BB').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BC').should('be.visible'); - - cy.get('body').click(0, 0); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('BBB') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - e2e.components.Select.option().should('have.length', 10); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBA').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBB').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('BBC').should('be.visible'); - }); -}); diff --git a/e2e/dashboards-suite/snapshot-create.spec.ts b/e2e/dashboards-suite/snapshot-create.spec.ts deleted file mode 100644 index ee07081bfd0..00000000000 --- a/e2e/dashboards-suite/snapshot-create.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { e2e } from '../utils'; - -describe('Snapshots', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Create a snapshot dashboard', () => { - // Opening a dashboard - cy.intercept({ - pathname: '/api/ds/query', - }).as('query'); - e2e.flows.openDashboard({ uid: 'ZqZnVvFZz', queryParams: { '__feature.newDashboardSharingComponent': false } }); - cy.wait('@query'); - - const panelsToCheck = [ - 'Raw Data Graph', - 'Last non-null', - 'min', - 'Max', - 'The data from graph above with seriesToColumns transform', - ]; - - // Open the sharing modal - e2e.components.NavToolbar.shareDashboard().click(); - - // Select the snapshot tab - e2e.components.Tab.title('Snapshot').click(); - - // Publish snapshot - cy.intercept('POST', '/api/snapshots').as('save'); - e2e.pages.ShareDashboardModal.SnapshotScene.PublishSnapshot().click(); - cy.wait('@save'); - - // Copy link button should be visible - e2e.pages.ShareDashboardModal.SnapshotScene.CopyUrlButton().should('exist'); - - // Copy the snapshot URL form the input and open the snapshot - e2e.pages.ShareDashboardModal.SnapshotScene.CopyUrlInput() - .invoke('val') - .then((text) => cy.wrap(text).as('url')); - - // Open the snapshot using the new URL - cy.get('@url').then((url) => { - e2e.pages.ShareDashboardModal.SnapshotScene.visit(getSnapshotKey(String(url))); - }); - - // Validate the dashboard controls are rendered - e2e.pages.Dashboard.Controls().should('exist'); - - // Validate the panels are rendered - for (const title of panelsToCheck) { - e2e.components.Panels.Panel.title(title).should('be.visible'); - } - }); -}); -// -const getSnapshotKey = (url: string): string => { - return url.split('/').pop(); -}; diff --git a/e2e/dashboards-suite/templating-dashboard-links-and-variables.spec.ts b/e2e/dashboards-suite/templating-dashboard-links-and-variables.spec.ts deleted file mode 100644 index 03fa10a4b2d..00000000000 --- a/e2e/dashboards-suite/templating-dashboard-links-and-variables.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { e2e } from '../utils'; - -describe('Templating', () => { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('Tests dashboard links and variables in links', () => { - cy.intercept({ - method: 'GET', - url: '/api/search?tag=templating&limit=100', - }).as('tagsTemplatingSearch'); - cy.intercept({ - method: 'GET', - url: '/api/search?tag=demo&limit=100', - }).as('tagsDemoSearch'); - - e2e.flows.openDashboard({ uid: 'yBCC3aKGk' }); - - // waiting for network requests first - cy.wait(['@tagsTemplatingSearch', '@tagsDemoSearch']); - - const verifyLinks = (variableValue: string) => { - e2e.components.DashboardLinks.link() - .should('be.visible') - .should((links) => { - expect(links).to.have.length.greaterThan(13); - - for (let index = 0; index < links.length; index++) { - expect(Cypress.$(links[index]).attr('href')).contains(variableValue); - } - }); - }; - - e2e.components.DashboardLinks.dropDown().should('be.visible').click().wait('@tagsTemplatingSearch'); - - verifyLinks('var-custom=$__all'); - - cy.get('body').click(); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('$__all') - .should('be.visible') - .within(() => { - cy.get('input').click(); - }); - - e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('p2').should('be.visible').click(); - - cy.get('body').click(); - - e2e.components.DashboardLinks.dropDown() - .scrollIntoView() - .should('be.visible') - .click() - .wait('@tagsTemplatingSearch'); - - // verify all links, should have p2 value - verifyLinks('p2'); - }); -}); diff --git a/e2e/dashboards-suite/textbox-variables.spec.ts b/e2e/dashboards-suite/textbox-variables.spec.ts deleted file mode 100644 index 4b5790a7f76..00000000000 --- a/e2e/dashboards-suite/textbox-variables.spec.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { e2e } from '../utils'; - -const PAGE_UNDER_TEST = 'AejrN1AMz'; - -describe('TextBox - load options scenarios', function () { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('default options should be correct', function () { - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}/templating-textbox-e2e-scenarios?orgId=1` }); - - validateTextboxAndMarkup('default value'); - }); - - it('loading variable from url should be correct', function () { - e2e.flows.openDashboard({ - uid: `${PAGE_UNDER_TEST}/templating-textbox-e2e-scenarios?orgId=1&var-text=not default value`, - }); - - validateTextboxAndMarkup('not default value'); - }); -}); - -describe.skip('TextBox - change query scenarios', function () { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('when changing the query value and not saving current as default should revert query value', function () { - copyExistingDashboard(); - - changeQueryInput(); - - e2e.components.BackButton.backArrow().should('be.visible').click({ force: true }); - - validateTextboxAndMarkup('changed value'); - - saveDashboard(false); - - cy.get('@dashuid').then((dashuid) => { - expect(dashuid).not.to.eq(PAGE_UNDER_TEST); - - e2e.flows.openDashboard({ uid: dashuid }); - - cy.wait('@load-dash'); - - validateTextboxAndMarkup('default value'); - - validateVariable('changed value'); - }); - }); - - it('when changing the query value and saving current as default should change query value', function () { - copyExistingDashboard(); - - changeQueryInput(); - - e2e.components.BackButton.backArrow().should('be.visible').click({ force: true }); - - validateTextboxAndMarkup('changed value'); - - saveDashboard(true); - - cy.get('@dashuid').then((dashuid) => { - expect(dashuid).not.to.eq(PAGE_UNDER_TEST); - - e2e.flows.openDashboard({ uid: dashuid }); - - cy.wait('@load-dash'); - - validateTextboxAndMarkup('changed value'); - - validateVariable('changed value'); - }); - }); -}); - -describe.skip('TextBox - change picker value scenarios', function () { - beforeEach(() => { - e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); - }); - - it('when changing the input value and not saving current as default should revert query value', function () { - copyExistingDashboard(); - - changeTextBoxInput(); - - validateTextboxAndMarkup('changed value'); - - saveDashboard(false); - - cy.get('@dashuid').then((dashuid) => { - expect(dashuid).not.to.eq(PAGE_UNDER_TEST); - - e2e.flows.openDashboard({ uid: dashuid }); - - cy.wait('@load-dash'); - - validateTextboxAndMarkup('default value'); - validateVariable('default value'); - }); - }); - - it('when changing the input value and saving current as default should change query value', function () { - copyExistingDashboard(); - - changeTextBoxInput(); - - validateTextboxAndMarkup('changed value'); - - saveDashboard(true); - - cy.get('@dashuid').then((dashuid) => { - expect(dashuid).not.to.eq(PAGE_UNDER_TEST); - - e2e.flows.openDashboard({ uid: dashuid }); - - cy.wait('@load-dash'); - - validateTextboxAndMarkup('changed value'); - validateVariable('changed value'); - }); - }); -}); - -function copyExistingDashboard() { - cy.intercept({ - method: 'GET', - url: '/api/search?query=&type=dash-folder&permission=Edit', - }).as('dash-settings'); - cy.intercept({ - method: 'POST', - url: '/api/dashboards/db/', - }).as('save-dash'); - cy.intercept({ - method: 'GET', - url: /\/api\/dashboards\/uid\/(?!AejrN1AMz)\w+/, - }).as('load-dash'); - e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}/templating-textbox-e2e-scenarios?orgId=1&editview=settings` }); - - cy.wait('@dash-settings'); - - e2e.pages.Dashboard.Settings.General.saveAsDashBoard().should('be.visible').click(); - - e2e.pages.SaveDashboardAsModal.newName().should('be.visible').type(`${Date.now()}`); - - e2e.pages.SaveDashboardAsModal.save().should('be.visible').click(); - - cy.wait('@save-dash'); - cy.wait('@load-dash'); - - e2e.pages.Dashboard.SubMenu.submenuItem().should('be.visible'); - - cy.location().then((loc) => { - const dashuid = /\/d\/(\w+)\//.exec(loc.href)![1]; - cy.wrap(dashuid).as('dashuid'); - }); - - cy.wait(500); -} - -function saveDashboard(saveVariables: boolean) { - e2e.components.PageToolbar.item('Save dashboard').should('be.visible').click(); - - if (saveVariables) { - e2e.pages.SaveDashboardModal.saveVariables().should('exist').click({ force: true }); - } - - e2e.pages.SaveDashboardModal.save().should('be.visible').click(); - - cy.wait('@save-dash'); -} - -function validateTextboxAndMarkup(value: string) { - e2e.pages.Dashboard.SubMenu.submenuItem() - .should('be.visible') - .within(() => { - e2e.pages.Dashboard.SubMenu.submenuItemLabels('text').should('be.visible'); - cy.get('input').should('be.visible').should('have.value', value); - }); - - e2e.components.Panels.Visualization.Text.container() - .should('be.visible') - .within(() => { - cy.get('h1').should('be.visible').should('have.text', `variable: ${value}`); - }); -} - -function validateVariable(value: string) { - e2e.components.PageToolbar.item('Dashboard settings').should('be.visible').click(); - - e2e.pages.Dashboard.Settings.General.sectionItems('Variables').should('be.visible').click(); - - e2e.pages.Dashboard.Settings.Variables.List.tableRowNameFields('text').should('be.visible').click(); - - e2e.pages.Dashboard.Settings.Variables.Edit.TextBoxVariable.textBoxOptionsQueryInputV2() - .should('be.visible') - .should('have.value', value); -} - -function changeTextBoxInput() { - e2e.pages.Dashboard.SubMenu.submenuItemLabels('text').should('be.visible'); - e2e.pages.Dashboard.SubMenu.submenuItem() - .should('be.visible') - .within(() => { - cy.get('input') - .should('be.visible') - .should('have.value', 'default value') - .clear() - .type('changed value') - .type('{enter}'); - }); - - cy.location().should((loc) => { - expect(loc.search).to.contain('var-text=changed%20value'); - }); -} - -function changeQueryInput() { - e2e.components.PageToolbar.item('Dashboard settings').should('be.visible').click(); - - e2e.pages.Dashboard.Settings.General.sectionItems('Variables').should('be.visible').click(); - - e2e.pages.Dashboard.Settings.Variables.List.tableRowNameFields('text').should('be.visible').click(); - - e2e.pages.Dashboard.Settings.Variables.Edit.TextBoxVariable.textBoxOptionsQueryInputV2() - .should('be.visible') - .clear() - .type('changed value') - .blur(); - - e2e.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption() - .should('have.length', 1) - .should('have.text', 'changed value'); -} diff --git a/e2e/dashboards-suite/utils/makeDashboard.ts b/e2e/dashboards-suite/utils/makeDashboard.ts deleted file mode 100644 index 2ada48db7a9..00000000000 --- a/e2e/dashboards-suite/utils/makeDashboard.ts +++ /dev/null @@ -1,58 +0,0 @@ -export function makeNewDashboardRequestBody(dashboardName: string, folderUid?: string) { - return { - dashboard: { - annotations: { - list: [ - { - builtIn: 1, - datasource: { type: 'grafana', uid: '-- Grafana --' }, - enable: true, - hide: true, - iconColor: 'rgba(0, 211, 255, 1)', - name: 'Annotations & Alerts', - type: 'dashboard', - }, - ], - }, - editable: true, - fiscalYearStartMonth: 0, - graphTooltip: 0, - links: [], - liveNow: false, - panels: [ - { - datasource: { type: 'testdata', uid: '89_jzlT4k' }, - gridPos: { h: 9, w: 12, x: 0, y: 0 }, - id: 2, - options: { - code: { - language: 'plaintext', - showLineNumbers: false, - showMiniMap: false, - }, - content: '***A nice little happy empty dashboard***', - mode: 'markdown', - }, - pluginVersion: '9.4.0-pre', - title: 'Nothing to see here', - type: 'text', - }, - ], - refresh: '', - revision: 1, - schemaVersion: 38, - tags: [], - templating: { list: [] }, - time: { from: 'now-6h', to: 'now' }, - timepicker: {}, - timezone: '', - title: dashboardName, - version: 0, - uid: '', - weekStart: '', - }, - message: '', - overwrite: false, - folderUid, - } as const; -} diff --git a/e2e/old-arch/various-suite/helpers/prometheus-helpers.ts b/e2e/old-arch/various-suite/helpers/prometheus-helpers.ts index 265095ecd65..0102bcec3ab 100644 --- a/e2e/old-arch/various-suite/helpers/prometheus-helpers.ts +++ b/e2e/old-arch/various-suite/helpers/prometheus-helpers.ts @@ -24,11 +24,11 @@ export function createPromDS(dataSourceID: string, name: string): void { } export function getResources() { - cy.intercept(/__name__/g, metricResponse); + cy.intercept(/__name__/g, metricResponse).as('getMetricNames'); - cy.intercept(/metadata/g, metadataResponse); + cy.intercept(/metadata/g, metadataResponse).as('getMetadata'); - cy.intercept(/labels/g, labelsResponse); + cy.intercept(/labels/g, labelsResponse).as('getLabels'); } const metricResponse = { diff --git a/e2e/old-arch/various-suite/prometheus-editor.spec.ts b/e2e/old-arch/various-suite/prometheus-editor.spec.ts index 4a2b5aed7a3..7b3093f4f5d 100644 --- a/e2e/old-arch/various-suite/prometheus-editor.spec.ts +++ b/e2e/old-arch/various-suite/prometheus-editor.spec.ts @@ -138,12 +138,11 @@ describe('Prometheus query editor', () => { it('can select a metric and provide a hint', () => { navigateToEditor('Builder', 'prometheusBuilder'); - getResources(); - - e2e.components.DataSource.Prometheus.queryEditor.builder.metricSelect().should('exist').click().type('metric1'); + e2e.components.DataSource.Prometheus.queryEditor.builder.metricSelect().should('exist').click(); + cy.wait('@getMetadata'); + e2e.components.DataSource.Prometheus.queryEditor.builder.metricSelect().type('metric1'); selectOption('metric1'); - e2e.components.DataSource.Prometheus.queryEditor.builder.hints().contains('hint: add rate'); }); diff --git a/e2e/run-suite b/e2e/run-suite index 3303918e1fd..f0b9fdd6712 100755 --- a/e2e/run-suite +++ b/e2e/run-suite @@ -28,7 +28,6 @@ declare -A env=( testFilesForSingleSuite="*.spec.ts" rootForEnterpriseSuite="./e2e/extensions" rootForOldArch="./e2e/old-arch" -rootForKubernetesDashboards="./e2e/dashboards-suite" rootForSearchDashboards="./e2e/dashboards-search-suite" declare -A cypressConfig=( @@ -111,24 +110,6 @@ case "$1" in cypressConfig[video]=${args[1]} env[dashboardScene]=false ;; - "dashboards-schema-v2") - env[kubernetesDashboards]=true - cypressConfig[specPattern]=$rootForKubernetesDashboards/$testFilesForSingleSuite - cypressConfig[video]=false - case "$2" in - "debug") - echo -e "Debug mode" - env[SLOWMO]=1 - PARAMS="--no-exit" - enterpriseSuite=$(basename "${args[2]}") - ;; - "dev") - echo "Dev mode" - CMD="cypress open" - enterpriseSuite=$(basename "${args[2]}") - ;; - esac - ;; "dashboards-search") env[kubernetesDashboards]=true cypressConfig[specPattern]=$rootForSearchDashboards/$testFilesForSingleSuite diff --git a/go.mod b/go.mod index f8c2a0a7b16..03f4d527c89 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( github.com/blugelabs/bluge v0.2.2 // @grafana/grafana-backend-group github.com/blugelabs/bluge_segment_api v0.2.0 // @grafana/grafana-backend-group github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // @grafana/grafana-backend-group - github.com/bwmarrin/snowflake v0.3.0 // @grafan/grafana-app-platform-squad + github.com/bwmarrin/snowflake v0.3.0 // @grafana/grafana-app-platform-squad github.com/centrifugal/centrifuge v0.35.0 // @grafana/grafana-app-platform-squad github.com/crewjam/saml v0.4.14 // @grafana/identity-access-team github.com/dgraph-io/badger/v4 v4.7.0 // @grafana/grafana-search-and-storage @@ -68,7 +68,7 @@ require ( github.com/go-openapi/strfmt v0.23.0 // @grafana/alerting-backend github.com/go-redis/redis/v8 v8.11.5 // @grafana/grafana-backend-group github.com/go-sourcemap/sourcemap v2.1.4+incompatible // @grafana/grafana-backend-group - github.com/go-sql-driver/mysql v1.9.2 // @grafana/grafana-search-and-storage + github.com/go-sql-driver/mysql v1.9.3 // @grafana/grafana-search-and-storage github.com/go-stack/stack v1.8.1 // @grafana/grafana-backend-group github.com/gobwas/glob v0.2.3 // @grafana/grafana-backend-group github.com/gogo/protobuf v1.3.2 // @grafana/alerting-backend @@ -97,7 +97,7 @@ require ( github.com/grafana/grafana-api-golang-client v0.27.0 // @grafana/alerting-backend github.com/grafana/grafana-app-sdk v0.40.2 // @grafana/grafana-app-platform-squad github.com/grafana/grafana-app-sdk/logging v0.40.1 // @grafana/grafana-app-platform-squad - github.com/grafana/grafana-aws-sdk v1.0.4 // @grafana/aws-datasources + github.com/grafana/grafana-aws-sdk v1.1.0 // @grafana/aws-datasources github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 // @grafana/partner-datasources github.com/grafana/grafana-cloud-migration-snapshot v1.9.0 // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-google-sdk-go v0.4.1 // @grafana/partner-datasources @@ -168,6 +168,7 @@ require ( github.com/spyzhov/ajson v0.9.6 // @grafana/grafana-sharing-squad github.com/stretchr/testify v1.10.0 // @grafana/grafana-backend-group github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf // @grafana/grafana-backend-group + github.com/thomaspoignant/go-feature-flag v1.42.0 // @grafana/grafana-backend-group github.com/tjhop/slog-gokit v0.1.3 // @grafana/grafana-app-platform-squad github.com/ua-parser/uap-go v0.0.0-20250213224047-9c035f085b90 // @grafana/grafana-backend-group github.com/urfave/cli v1.22.16 // indirect; @grafana/grafana-backend-group @@ -229,24 +230,25 @@ require ( ) require ( - github.com/grafana/grafana/apps/advisor v0.0.0-20250627191313-2f1a6ae1712b // @grafana/plugins-platform-backend - github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250627191313-2f1a6ae1712b // @grafana/alerting-backend - github.com/grafana/grafana/apps/dashboard v0.0.0-20250730164619-34019e5ec017 // @grafana/grafana-app-platform-squad @grafana/dashboards-squad - github.com/grafana/grafana/apps/folder v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-search-and-storage - github.com/grafana/grafana/apps/iam v0.0.0-20250627191313-2f1a6ae1712b // @grafana/identity-access-team - github.com/grafana/grafana/apps/investigations v0.0.0-20250627191313-2f1a6ae1712b // @fcjack @matryer - github.com/grafana/grafana/apps/playlist v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad - github.com/grafana/grafana/apps/secret v0.0.0-20250731151929-0aac22a9e2d3 // @grafana/grafana-operator-experience-squad - github.com/grafana/grafana/pkg/aggregator v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad - github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250711114246-c9b2126c4ad5 // @grafana/grafana-app-platform-squad - github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/apps/advisor v0.0.0-20250804150913-990f1c69ecc2 // @grafana/plugins-platform-backend + github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250804150913-990f1c69ecc2 // @grafana/alerting-backend + github.com/grafana/grafana/apps/dashboard v0.0.0-20250804150913-990f1c69ecc2 // @grafana/grafana-app-platform-squad @grafana/dashboards-squad + github.com/grafana/grafana/apps/folder v0.0.0-20250804150913-990f1c69ecc2 // @grafana/grafana-search-and-storage + github.com/grafana/grafana/apps/iam v0.0.0-20250804150913-990f1c69ecc2 // @grafana/identity-access-team + github.com/grafana/grafana/apps/investigations v0.0.0-20250804150913-990f1c69ecc2 // @fcjack @matryer + github.com/grafana/grafana/apps/playlist v0.0.0-20250804150913-990f1c69ecc2 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/apps/provisioning v0.0.0-20250804150913-990f1c69ecc2 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/apps/secret v0.0.0-20250804150913-990f1c69ecc2 // @grafana/grafana-operator-experience-squad + github.com/grafana/grafana/apps/shorturl v0.0.0-20250804150913-990f1c69ecc2 // @grafana/sharing-squad + github.com/grafana/grafana/pkg/aggregator v0.0.0-20250804150913-990f1c69ecc2 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana/pkg/apiserver v0.0.0-20250804150913-990f1c69ecc2 // @grafana/grafana-app-platform-squad // This needs to be here for other projects that import grafana/grafana // For local development grafana/grafana will always use the local files // Check go.work file for details github.com/grafana/grafana/pkg/promlib v0.0.8 // @grafana/oss-big-tent - github.com/grafana/grafana/pkg/semconv v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-app-platform-squad - github.com/thomaspoignant/go-feature-flag v1.42.0 // @grafana/grafana-backend-group + github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 // @grafana/grafana-app-platform-squad ) require ( @@ -406,7 +408,7 @@ require ( github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 // indirect github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect - github.com/grafana/sqlds/v4 v4.2.3 // indirect + github.com/grafana/sqlds/v4 v4.2.4 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect github.com/hashicorp/consul/api v1.31.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect diff --git a/go.sum b/go.sum index d3ce4af5ca7..9e576825284 100644 --- a/go.sum +++ b/go.sum @@ -1604,8 +1604,8 @@ github.com/grafana/grafana-app-sdk v0.40.2 h1:j2ftFuqhX+exYUipfEjeWDs3i7oiJkweTF github.com/grafana/grafana-app-sdk v0.40.2/go.mod h1:BbNXPNki3mtbkWxYqJsyA1Cj9AShSyaY33z8WkyfVv0= github.com/grafana/grafana-app-sdk/logging v0.40.1 h1:ru+GqbaQk6jthA5l2Yo1WI/JbNXKNQmLiqNrxz7HGP4= github.com/grafana/grafana-app-sdk/logging v0.40.1/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU= -github.com/grafana/grafana-aws-sdk v1.0.4 h1:D14UAehsOqpjliHmHzveRQ1p43KCsMzdmb7GovWj+SY= -github.com/grafana/grafana-aws-sdk v1.0.4/go.mod h1:hO7q7yWV+t6dmiyJjMa3IbuYnYkBua+G/IAlOPVIYKE= +github.com/grafana/grafana-aws-sdk v1.1.0 h1:G0fvwbQmHw14c5RXPd7Gnw9ZQcgzl139LtMDoe0KhmE= +github.com/grafana/grafana-aws-sdk v1.1.0/go.mod h1:7e+47EdHynteYWGoT5Ere9KeOXQObsk8F0vkOLQ1tz8= github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 h1:0TYrkzAc3u0HX+9GK86cGrLTUAcmQfl3/LEB3tL+SOA= github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0/go.mod h1:H9sVh9A4yg5egMGZeh0mifxT1Q/uqwKe1LBjBJU6pN8= github.com/grafana/grafana-cloud-migration-snapshot v1.9.0 h1:JOzchPgptwJdruYoed7x28lFDwhzs7kssResYsnC0iI= @@ -1616,32 +1616,36 @@ github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79/go.mod h1:wc6Hbh3K2TgCUSfBC/BOzabItujtHMESZeFk5ZhdxhQ= github.com/grafana/grafana-plugin-sdk-go v0.278.0 h1:5/rIYparLi02pofdaag8wnjspMMVNCi8cZhC4cdC3Ho= github.com/grafana/grafana-plugin-sdk-go v0.278.0/go.mod h1:+8NXT/XUJ/89GV6FxGQ366NZ3nU+cAXDMd0OUESF9H4= -github.com/grafana/grafana/apps/advisor v0.0.0-20250627191313-2f1a6ae1712b h1:8o0CqX6gMyf0dertWTVs0dsRwEI9ajlWsUDFKLkiug8= -github.com/grafana/grafana/apps/advisor v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:q+h3HbmqU/PposW6lq8cMle1v8vuyX1LCMrGzbabHxc= -github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250627191313-2f1a6ae1712b h1:jr+C3epmjhd5Yyob4P1Z/dPaW4LRTkU5UJLXsI4eaeM= -github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:WpI7TCck4P2wKTO2WJLBRcfOWvUGvTdxYu3QqS3z7jM= -github.com/grafana/grafana/apps/dashboard v0.0.0-20250730164619-34019e5ec017 h1:Niy+KRDWHsUVqfhZQg0oZbAQFO6QcO6a4l9V/ouDEEs= -github.com/grafana/grafana/apps/dashboard v0.0.0-20250730164619-34019e5ec017/go.mod h1:/iuseD/cEpXDiy7MpL+4qBFZ3H6esnUJTYzpoJMw9dw= -github.com/grafana/grafana/apps/folder v0.0.0-20250627191313-2f1a6ae1712b h1:31MwoIKKT9Ay0ZjbT4lkfcPijiWogUWzXs2EjrCgodI= -github.com/grafana/grafana/apps/folder v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:dLtYBp1pza5HYalezNvzlP8JDeKrZ5BKTonDgEOE0NY= -github.com/grafana/grafana/apps/iam v0.0.0-20250627191313-2f1a6ae1712b h1:NV8v9xdM/pzjjy+1cLqUseia3bYcvQGh88vZdMW/jA0= -github.com/grafana/grafana/apps/iam v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:tDBCTbGRvjHTFgMc06hRRmceGSmoub7cReUMbHHS56Q= -github.com/grafana/grafana/apps/investigations v0.0.0-20250627191313-2f1a6ae1712b h1:5kTINZGOt6RpiSAfzZEksvpBfOKphylZ7u3Tnm0OaKo= -github.com/grafana/grafana/apps/investigations v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:8RlQ4U9lccPEBD/QxV4zyIMh9+lzjS/7xGpiqn3cHLY= -github.com/grafana/grafana/apps/playlist v0.0.0-20250627191313-2f1a6ae1712b h1:elfpvk06igCjE0yL+/urc69UDOt1B/sPfdNg9X9kUMc= -github.com/grafana/grafana/apps/playlist v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:fPtx6dwGm0PweQRVbgtthMapJMvXobBcORbndb7Dgd4= -github.com/grafana/grafana/apps/secret v0.0.0-20250731151929-0aac22a9e2d3 h1:16eaVEucbwis3TxS4CYZxxg5wfPAP/6u7Ji2+wbiHyk= -github.com/grafana/grafana/apps/secret v0.0.0-20250731151929-0aac22a9e2d3/go.mod h1:pS2M5ILsHx9VNTM96glLtCjCVXHWyfGcT34WHvbbMtM= -github.com/grafana/grafana/pkg/aggregator v0.0.0-20250627191313-2f1a6ae1712b h1:ei01IFqmnXkOrrVvsT3CYe+i5xYra3SCX7Wsu3PMsDU= -github.com/grafana/grafana/pkg/aggregator v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:+H4Va9jDJlGQJjAN+OFD/hLx2I/yEzDRMQLaKecvgAc= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250711114246-c9b2126c4ad5 h1:f4fopIH6eQRoZ/E7bstn69UtDAHleIdQ6DrdzEs++Ug= -github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250711114246-c9b2126c4ad5/go.mod h1:eAlOam2uWhrsEZlOoAr7XZ9hbBP7SyYGYn31/aQAPs8= -github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b h1:QyJLJn3xwFTIXu9KPZujsrIUN0X8DdiR9b2h75L0AfI= -github.com/grafana/grafana/pkg/apiserver v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:6OKkPWDB8PetDXqMVMOWL35iTCEUdpATwwpuew0k8+o= +github.com/grafana/grafana/apps/advisor v0.0.0-20250804150913-990f1c69ecc2 h1:APintn9iX4pIRrDBGmxGf+d8PekMNtt36zb4rACGQgY= +github.com/grafana/grafana/apps/advisor v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:5qLtZ9W2PlDZa2Wxq3YaK1v35GIZmPNP0m8u8HhTYfI= +github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250804150913-990f1c69ecc2 h1:bZ61Ik/7FQaF5c97u5PSJ+SJLSjQ6Q9zBO3GRDuzajI= +github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:SR1zEHjyL30eZIuwxXLr9ubUKzidQrNEtFNn6wW4ZxE= +github.com/grafana/grafana/apps/dashboard v0.0.0-20250804150913-990f1c69ecc2 h1:M5kMQVRjcjtnGuw0RM9/iab5VpmJyBwpEDbSfS6IQV4= +github.com/grafana/grafana/apps/dashboard v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:Ik6PLuCNceqTeOesGeQLn4hVL5wCbfscuAEhBEdq8js= +github.com/grafana/grafana/apps/folder v0.0.0-20250804150913-990f1c69ecc2 h1:tubqrwcZaJzS3DM2bLJxK6OCUsmBK0Vs95HiBuhCQcU= +github.com/grafana/grafana/apps/folder v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:la6q+Qvy0JGzBu/yyjC+e7xPefPM7CEfEgD574R2QMU= +github.com/grafana/grafana/apps/iam v0.0.0-20250804150913-990f1c69ecc2 h1:T55BYKx/s2cWfGRtBqYAlqqgE+rQIEV56OxYEhtCXH8= +github.com/grafana/grafana/apps/iam v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:6dF/R8xe/zkVIlbKCRjXsfhxA3ZsNiQf46S9/ezEEKI= +github.com/grafana/grafana/apps/investigations v0.0.0-20250804150913-990f1c69ecc2 h1:2xEMvfEqgYc/gunmHmsw51ZMVCdEvsqJTg3qaaqh8nw= +github.com/grafana/grafana/apps/investigations v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:IOpGVuSwFWE7EMrooI8I6OIU+rOmJinOaxK3pVMl7No= +github.com/grafana/grafana/apps/playlist v0.0.0-20250804150913-990f1c69ecc2 h1:Bt9EQkYdYq+xumbEnNxbbz0in7JRJkK9lVFs49WEetE= +github.com/grafana/grafana/apps/playlist v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:M1ily25xZS7sZlQ5yK1HV0UjRme6lpOl6I3dcfyqqRo= +github.com/grafana/grafana/apps/provisioning v0.0.0-20250804150913-990f1c69ecc2 h1:83OGBndFNYijNjgQpSf4y1bSNlscVDq8ru97Q6PyU6o= +github.com/grafana/grafana/apps/provisioning v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:qzFUVwLI1b5UIbVFxFydUYAsnOK27AgtA5so3EW8jM0= +github.com/grafana/grafana/apps/secret v0.0.0-20250804150913-990f1c69ecc2 h1:r5oGyvRVxljzKHTQYHtpmK2Et+BVhWQVw7aXo+FUygk= +github.com/grafana/grafana/apps/secret v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:pS2M5ILsHx9VNTM96glLtCjCVXHWyfGcT34WHvbbMtM= +github.com/grafana/grafana/apps/shorturl v0.0.0-20250804150913-990f1c69ecc2 h1:n+PKetgDEzRYaenMmz5gXXrQMLScORlO+ZCnriZucQw= +github.com/grafana/grafana/apps/shorturl v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:6F043DPw1shYLpIAAlbww4BMDtRtVX5MetfUf0s2zoA= +github.com/grafana/grafana/pkg/aggregator v0.0.0-20250804150913-990f1c69ecc2 h1:1AdHE94bYfEG7cuFb92wFlBaVPeexF21l9zCFmN8im0= +github.com/grafana/grafana/pkg/aggregator v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:wU4CoDIHuOOL7ZnchyvfER5UAcEz94A+ZkY+Xt+Ks/8= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 h1:X0cnaFdR+iz+sDSuoZmkryFSjOirchHe2MdKSRwBWgM= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:RRvSjHH12/PnQaXraMO65jUhVu8n59mzvhfIMBETnV4= +github.com/grafana/grafana/pkg/apiserver v0.0.0-20250804150913-990f1c69ecc2 h1:oUINBBiA25EwjOFXJVa4rmU8jHlOGn5EdPCmqpIqy4Q= +github.com/grafana/grafana/pkg/apiserver v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:BA9Rvm6co9tbrZ0lS8ScqLFvAnDQ4lZOyplJgB+yMQQ= github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0= github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao= -github.com/grafana/grafana/pkg/semconv v0.0.0-20250627191313-2f1a6ae1712b h1:m78RNSvTseSpvwQYe5HcdsiADSw4vj6+QUppuCL63gw= -github.com/grafana/grafana/pkg/semconv v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:mu3yl0GxB0eQZV1q7Kka0pkF3Th9x7W04WrjR9wqBlc= +github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 h1:A65jWgLk4Re28gIuZcpC0aTh71JZ0ey89hKGE9h543s= +github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:2HRzUK/xQEYc+8d5If/XSusMcaYq9IptnBSHACiQcOQ= github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 h1:NznuPwItog+rwdVg8hAuGKP29ndRSzJAwhxKldkP8oQ= github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32/go.mod h1:796sq+UcONnSlzA3RtlBZ+b/hrerkZXiEmO8oMjyRwY= github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 h1:ZYk42718kSXOiIKdjZKljWLgBpzL5z1yutKABksQCMg= @@ -1662,8 +1666,8 @@ github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrR github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56 h1:SDGrP81Vcd102L3UJEryRd1eestRw73wt+b8vnVEFe0= github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56/go.mod h1:S4+611dxnKt8z/ulbvaJzcgSHsuhjVc1QHNTcr1R7Fw= -github.com/grafana/sqlds/v4 v4.2.3 h1:9ibD1c5O5u9fifEkBSig+jAc41TUEz+M+bWQqDsofP4= -github.com/grafana/sqlds/v4 v4.2.3/go.mod h1:bv+XHabfUF4xkgg4y+nYFCK8rpMHZsMaQk56qNaJcAM= +github.com/grafana/sqlds/v4 v4.2.4 h1:Xlxy1udWqDK0dlbuJ1qXL7K3EYaf+aKMl38zhd3VbQY= +github.com/grafana/sqlds/v4 v4.2.4/go.mod h1:BQRjUG8rOqrBI4NAaeoWrIMuoNgfi8bdhCJ+5cgEfLU= github.com/grafana/tempo v1.5.1-0.20250529124718-87c2dc380cec h1:wnzJov9RhSHGaTYGzTygL4qq986fLen8xSqnQgaMd28= github.com/grafana/tempo v1.5.1-0.20250529124718-87c2dc380cec/go.mod h1:j1IY7J2rUz7TcTjFVVx6HCpyTlYOJPtXuGRZ7sI+vSo= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= diff --git a/go.work b/go.work index 2735423d372..51f59db084a 100644 --- a/go.work +++ b/go.work @@ -12,7 +12,9 @@ use ( ./apps/iam ./apps/investigations ./apps/playlist + ./apps/provisioning ./apps/secret + ./apps/shorturl ./pkg/aggregator ./pkg/apimachinery ./pkg/apiserver diff --git a/go.work.sum b/go.work.sum index d129bd22b1f..9d0c865b9f1 100644 --- a/go.work.sum +++ b/go.work.sum @@ -834,6 +834,7 @@ github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40 github.com/expr-lang/expr v1.17.0/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/fgprof v0.9.4 h1:ocDNwMFlnA0NU0zSB3I52xkO4sFXk80VK9lXjLClu88= @@ -920,7 +921,6 @@ github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMc github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= @@ -987,16 +987,30 @@ github.com/grafana/grafana-plugin-sdk-go v0.275.0/go.mod h1:mO9LJqdXDh5JpO/xIdPA github.com/grafana/grafana-plugin-sdk-go v0.277.0/go.mod h1:mAUWg68w5+1f5TLDqagIr8sWr1RT9h7ufJl5NMcWJAU= github.com/grafana/grafana/apps/advisor v0.0.0-20250123151950-b066a6313173/go.mod h1:goSDiy3jtC2cp8wjpPZdUHRENcoSUHae1/Px/MDfddA= github.com/grafana/grafana/apps/advisor v0.0.0-20250220154326-6e5de80ef295/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c= +github.com/grafana/grafana/apps/advisor v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:5qLtZ9W2PlDZa2Wxq3YaK1v35GIZmPNP0m8u8HhTYfI= github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250121113133-e747350fee2d/go.mod h1:AvleS6icyPmcBjihtx5jYEvdzLmHGBp66NuE0AMR57A= github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250416173722-ec17e0e4ce03/go.mod h1:oemrhKvFxxc5m32xKHPxInEHAObH0/hPPyHUiBUZ1Cc= github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250506052906-7a2fc797fb4a/go.mod h1:VkX53kBiqIMHBoGgeEDJnzm5Nwcmv/726tuZuT5SvJY= +github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:SR1zEHjyL30eZIuwxXLr9ubUKzidQrNEtFNn6wW4ZxE= github.com/grafana/grafana/apps/dashboard v0.0.0-20250616135341-59c2f154336b/go.mod h1:OIlvNnUufYDhBXa4xK4CyzPI2C69ZJkHy5+aFDyPtXw= github.com/grafana/grafana/apps/dashboard v0.0.0-20250616145019-8d27f12428cb/go.mod h1:OIlvNnUufYDhBXa4xK4CyzPI2C69ZJkHy5+aFDyPtXw= github.com/grafana/grafana/apps/dashboard v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:eR8wca74ADgxBrvX0uNpdB1qnPaGx/KhCm4Xj8oqHfQ= +github.com/grafana/grafana/apps/dashboard v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:Ik6PLuCNceqTeOesGeQLn4hVL5wCbfscuAEhBEdq8js= +github.com/grafana/grafana/apps/folder v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:la6q+Qvy0JGzBu/yyjC+e7xPefPM7CEfEgD574R2QMU= +github.com/grafana/grafana/apps/iam v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:6dF/R8xe/zkVIlbKCRjXsfhxA3ZsNiQf46S9/ezEEKI= github.com/grafana/grafana/apps/investigation v0.0.0-20250121113133-e747350fee2d/go.mod h1:HQprw3MmiYj5OUV9CZnkwA1FKDZBmYACuAB3oDvUOmI= +github.com/grafana/grafana/apps/investigations v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:IOpGVuSwFWE7EMrooI8I6OIU+rOmJinOaxK3pVMl7No= github.com/grafana/grafana/apps/playlist v0.0.0-20250121113133-e747350fee2d/go.mod h1:DjJe5osrW/BKrzN9hAAOSElNWutj1bcriExa7iDP7kA= +github.com/grafana/grafana/apps/playlist v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:M1ily25xZS7sZlQ5yK1HV0UjRme6lpOl6I3dcfyqqRo= +github.com/grafana/grafana/apps/provisioning v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:qzFUVwLI1b5UIbVFxFydUYAsnOK27AgtA5so3EW8jM0= +github.com/grafana/grafana/apps/secret v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:pS2M5ILsHx9VNTM96glLtCjCVXHWyfGcT34WHvbbMtM= +github.com/grafana/grafana/apps/shorturl v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:6F043DPw1shYLpIAAlbww4BMDtRtVX5MetfUf0s2zoA= github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d/go.mod h1:1sq0guad+G4SUTlBgx7SXfhnzy7D86K/LcVOtiQCiMA= +github.com/grafana/grafana/pkg/aggregator v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:wU4CoDIHuOOL7ZnchyvfER5UAcEz94A+ZkY+Xt+Ks/8= +github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:RRvSjHH12/PnQaXraMO65jUhVu8n59mzvhfIMBETnV4= +github.com/grafana/grafana/pkg/apiserver v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:BA9Rvm6co9tbrZ0lS8ScqLFvAnDQ4lZOyplJgB+yMQQ= github.com/grafana/grafana/pkg/semconv v0.0.0-20250121113133-e747350fee2d/go.mod h1:tfLnBpPYgwrBMRz4EXqPCZJyCjEG4Ev37FSlXnocJ2c= +github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2/go.mod h1:2HRzUK/xQEYc+8d5If/XSusMcaYq9IptnBSHACiQcOQ= github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250121113133-e747350fee2d/go.mod h1:CXpwZ3Mkw6xVlGKc0SqUxqXCP3Uv182q6qAQnLaLxRg= github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:xrKQcxQxz+IUF90ybtfENFeEXtlj9nAsX/3Fw0KEIeQ= github.com/grafana/nanogit v0.0.0-20250616082354-5e94194d02ed h1:59JF1WhHLT+lNX89Tm1OzOEySMVMASAhaPbsRjtp8Kc= @@ -1009,6 +1023,7 @@ github.com/grafana/prometheus-alertmanager v0.25.1-0.20250331083058-4563aec7a975 github.com/grafana/prometheus-alertmanager v0.25.1-0.20250331083058-4563aec7a975/go.mod h1:FGdGvhI40Dq+CTQaSzK9evuve774cgOUdGfVO04OXkw= github.com/grafana/prometheus-alertmanager v0.25.1-0.20250604130045-92c8f6389b36 h1:AjZ58JRw1ZieFH/SdsddF5BXtsDKt5kSrKNPWrzYz3Y= github.com/grafana/prometheus-alertmanager v0.25.1-0.20250604130045-92c8f6389b36/go.mod h1:O/QP1BCm0HHIzbKvgMzqb5sSyH88rzkFk84F4TfJjBU= +github.com/grafana/sqlds/v4 v4.2.4 h1:Xlxy1udWqDK0dlbuJ1qXL7K3EYaf+aKMl38zhd3VbQY= github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0 h1:bjh0PVYSVVFxzINqPFYJmAmJNrWPgnVjuSdYJGHmtFU= github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0/go.mod h1:7t5XR+2IA8P2qggOAHTj/GCZfoLBle3OvNSYh1VkRBU= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= @@ -1206,10 +1221,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1 h1:dOYG7LS/WK00RWZc8XGgcUTlTxpp3mKhdR2Q9z9HbXM= github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/zipkinexporter v0.124.1 h1:+aiMrDR6xiaDM7xN4ByrBYI0Craqt68nZicmpYpt0co= github.com/open-telemetry/opentelemetry-collector-contrib/exporter/zipkinexporter v0.124.1/go.mod h1:H/TEWN4jgExt0McrtrBK2VFK6r9LRsWtqhEZrH690rs= github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.124.1 h1:NrjsoVPxI6lmV8jPImDcMeqYh+97Y71f/HB5Sfpfe3I= @@ -1741,6 +1754,7 @@ golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= @@ -1781,6 +1795,7 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e/go. google.golang.org/genproto/googleapis/api v0.0.0-20250425173222-7b384671a197/go.mod h1:Cd8IzgPo5Akum2c9R6FsXNaZbH3Jpa2gpHlW89FqlyQ= google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= google.golang.org/genproto/googleapis/api v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:W3S/3np0/dPWsWLi1h/UymYctGXaGBM2StwzD0y140U= +google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a/go.mod h1:a77HrdMjoeKbnd2jmgcWdaS++ZLZAEq3orIOAEIKiVw= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250505200425-f936aa4a68b2 h1:DbpkGFGRkd4GORg+IWQW2EhxUaa/My/PM8d1CGyTDMY= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= @@ -1802,6 +1817,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index aec836f5e3e..2e432ac72e4 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -87,6 +87,7 @@ grafana::codegen:run pkg grafana::codegen:run pkg/apimachinery grafana::codegen:run pkg/aggregator grafana::codegen:run apps/dashboard/pkg +grafana::codegen:run apps/provisioning/pkg grafana::codegen:run apps/folder/pkg if [ -d "pkg/extensions/apis" ]; then diff --git a/package.json b/package.json index 8a70fc05731..71c62b5c442 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dev": "NODE_ENV=dev nx exec -- webpack --config scripts/webpack/webpack.dev.js", "e2e": "./e2e/start-and-run-suite", "e2e:old-arch": "./e2e/start-and-run-suite old-arch", - "e2e:schema-v2": "./e2e/start-and-run-suite dashboards-schema-v2", + "e2e:schema-v2": "KUBERNETES_DASHBOARDS=true yarn playwright test --project dashboards", "e2e:dashboards-search": "./e2e/start-and-run-suite dashboards-search", "e2e:debug": "./e2e/start-and-run-suite debug", "e2e:dev": "./e2e/start-and-run-suite dev", @@ -101,14 +101,14 @@ "@react-types/menu": "3.10.3", "@react-types/overlays": "3.9.0", "@react-types/shared": "3.31.0", - "@rsdoctor/webpack-plugin": "^0.4.6", + "@rsdoctor/webpack-plugin": "^1.0.0", "@rtk-query/codegen-openapi": "^2.0.0", "@rtsao/plugin-proposal-class-properties": "7.0.1-patch.1", "@stylistic/eslint-plugin-ts": "^4.0.0", "@swc/core": "1.13.3", "@swc/helpers": "0.5.17", - "@testing-library/dom": "10.4.0", - "@testing-library/jest-dom": "6.6.3", + "@testing-library/dom": "10.4.1", + "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", "@types/babel__core": "^7", @@ -121,9 +121,9 @@ "@types/d3-scale-chromatic": "3.1.0", "@types/debounce-promise": "3.1.9", "@types/eslint": "9.6.1", - "@types/eslint-scope": "^3.7.7", + "@types/eslint-scope": "^8.0.0", "@types/file-saver": "2.0.7", - "@types/glob": "^8.0.0", + "@types/glob": "^9.0.0", "@types/google.analytics": "^0.0.46", "@types/gtag.js": "^0.0.20", "@types/history": "4.7.11", @@ -167,14 +167,14 @@ "@typescript-eslint/eslint-plugin": "8.38.0", "@typescript-eslint/parser": "8.38.0", "autoprefixer": "10.4.21", - "babel-loader": "9.2.1", + "babel-loader": "10.0.0", "blob-polyfill": "9.0.20240710", "browserslist": "^4.21.4", "chance": "^1.1.13", "chrome-remote-interface": "0.33.3", "codeowners": "^5.1.1", "confusing-browser-globals": "^1.0.11", - "copy-webpack-plugin": "12.0.2", + "copy-webpack-plugin": "13.0.0", "core-js": "3.44.0", "crashme": "0.0.15", "css-loader": "7.1.2", @@ -186,11 +186,11 @@ "esbuild-loader": "4.3.0", "esbuild-plugin-browserslist": "^1.0.0", "eslint": "9.32.0", - "eslint-config-prettier": "9.1.0", + "eslint-config-prettier": "10.1.8", "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jest": "28.11.0", + "eslint-plugin-jest": "29.0.1", "eslint-plugin-jest-dom": "^5.4.0", - "eslint-plugin-jsdoc": "50.6.3", + "eslint-plugin-jsdoc": "52.0.2", "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-lodash": "8.0.0", "eslint-plugin-no-barrel-files": "^1.1.1", @@ -250,7 +250,7 @@ "tracelib": "1.0.1", "ts-jest": "29.4.0", "ts-node": "10.9.2", - "typescript": "5.8.3", + "typescript": "5.9.2", "webpack": "5.101.0", "webpack-assets-manifest": "^5.1.0", "webpack-cli": "6.0.1", @@ -272,14 +272,14 @@ "@formatjs/intl-durationformat": "^0.7.0", "@glideapps/glide-data-grid": "^6.0.0", "@grafana/alerting": "workspace:*", - "@grafana/assistant": "0.0.12", + "@grafana/assistant": "0.0.13", "@grafana/aws-sdk": "0.7.1", "@grafana/azure-sdk": "0.0.7", "@grafana/data": "workspace:*", "@grafana/e2e-selectors": "workspace:*", - "@grafana/faro-core": "^1.13.2", - "@grafana/faro-web-sdk": "^1.13.2", - "@grafana/faro-web-tracing": "^1.13.2", + "@grafana/faro-core": "^1.19.0", + "@grafana/faro-web-sdk": "^1.19.0", + "@grafana/faro-web-tracing": "^1.19.0", "@grafana/flamegraph": "workspace:*", "@grafana/google-sdk": "0.3.4", "@grafana/i18n": "workspace:*", @@ -290,8 +290,8 @@ "@grafana/plugin-ui": "0.10.7", "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", - "@grafana/scenes": "6.28.6", - "@grafana/scenes-react": "6.28.6", + "@grafana/scenes": "6.29.1", + "@grafana/scenes-react": "6.29.1", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", diff --git a/packages/grafana-alerting/package.json b/packages/grafana-alerting/package.json index a777abcc684..27ba36f953a 100644 --- a/packages/grafana-alerting/package.json +++ b/packages/grafana-alerting/package.json @@ -78,7 +78,7 @@ "rollup-plugin-esbuild": "6.2.1", "rollup-plugin-node-externals": "^8.0.0", "type-fest": "^4.40.0", - "typescript": "5.8.3" + "typescript": "5.9.2" }, "peerDependencies": { "@grafana/runtime": ">=11.6 <= 12.x", diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 10a5d2e046f..77cbb8f36d6 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -100,7 +100,7 @@ "rollup": "^4.22.4", "rollup-plugin-esbuild": "6.2.1", "rollup-plugin-node-externals": "^8.0.0", - "typescript": "5.8.3" + "typescript": "5.9.2" }, "peerDependencies": { "react": "^18.0.0", diff --git a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts index e23c1ebf456..9b3956a12f2 100644 --- a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts +++ b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.test.ts @@ -21,7 +21,7 @@ describe('Grouping to Matrix', () => { const seriesA = toDataFrame({ name: 'A', fields: [ - { name: 'Time', type: FieldType.time, values: [1000, 1001, 1002] }, + { name: 'Time', type: FieldType.time, values: [1000, 1001, 1002], config: { interval: 60000 } }, { name: 'Value', type: FieldType.number, values: [1, 2, 3] }, ], }); @@ -33,7 +33,9 @@ describe('Grouping to Matrix', () => { name: 'Time\\Time', type: FieldType.time, values: [1000, 1001, 1002], - config: {}, + config: { + interval: 60000, + }, }, { name: '1000', diff --git a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts index 3415590d64b..2a9ae7eb03b 100644 --- a/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts +++ b/packages/grafana-data/src/transformations/transformers/groupingToMatrix.ts @@ -12,6 +12,7 @@ import { fieldMatchers } from '../matchers'; import { FieldMatcherID } from '../matchers/ids'; import { DataTransformerID } from './ids'; +import { getSpecialValue } from './utils'; export interface GroupingToMatrixTransformerOptions { columnField?: string; @@ -107,7 +108,7 @@ export const groupingToMatrixTransformer: DataTransformerInfo { ]); }); }); + + it('should fill in empty values with the indicated option', async () => { + const cfgC: DataTransformerConfig = { + id: DataTransformerID.transpose, + options: { + emptyValue: SpecialValue.Zero, + }, + }; + const seriesC = toDataFrame({ + name: 'C', + fields: [ + { name: 'A', type: FieldType.string, values: ['apple', undefined] }, + { name: 'B', type: FieldType.string, values: [undefined, 'orange'] }, + { name: 'C', type: FieldType.string, values: ['banana', undefined] }, + { name: 'D', type: FieldType.string, values: ['strawberry', 'pear'] }, + ], + }); + await expect(transformDataFrame([cfgC], [seriesC])).toEmitValuesWith((received) => { + const result = received[0]; + expect(result[0].fields).toEqual([ + { + name: 'Field', + type: FieldType.string, + values: ['B', 'C', 'D'], + config: {}, + }, + { + name: 'Value', + labels: { A: 'apple' }, + type: FieldType.string, + values: [0, 'banana', 'strawberry'], + config: {}, + }, + { + name: 'Value', + labels: { A: 0 }, + type: FieldType.string, + values: ['orange', 0, 'pear'], + config: {}, + }, + ]); + }); + }); }); diff --git a/packages/grafana-data/src/transformations/transformers/transpose.ts b/packages/grafana-data/src/transformations/transformers/transpose.ts index 651254a736d..aff84099192 100644 --- a/packages/grafana-data/src/transformations/transformers/transpose.ts +++ b/packages/grafana-data/src/transformations/transformers/transpose.ts @@ -2,13 +2,15 @@ import { map } from 'rxjs/operators'; import { cacheFieldDisplayNames } from '../../field/fieldState'; import { DataFrame, Field, FieldType } from '../../types/dataFrame'; -import { DataTransformerInfo } from '../../types/transformations'; +import { DataTransformerInfo, SpecialValue } from '../../types/transformations'; import { DataTransformerID } from './ids'; +import { getSpecialValue } from './utils'; export interface TransposeTransformerOptions { firstFieldName?: string; restFieldsName?: string; + emptyValue?: SpecialValue; } export const transposeTransformer: DataTransformerInfo = { @@ -30,6 +32,7 @@ export const transposeTransformer: DataTransformerInfo { const firstField = frame.fields[0]; @@ -38,7 +41,7 @@ function transposeDataFrame(options: TransposeTransformerOptions, data: DataFram const useFirstFieldAsHeaders = firstField.type === FieldType.string || firstField.type === FieldType.time || firstField.type === FieldType.enum; const headers = useFirstFieldAsHeaders - ? [firstName, ...fieldValuesAsStrings(firstField, firstField.values)] + ? [firstName, ...fieldValuesAsStrings(firstField, firstField.values, emptyValue)] : [firstName, ...firstField.values.map((_, i) => restName)]; const rows = useFirstFieldAsHeaders ? frame.fields @@ -65,7 +68,7 @@ function transposeDataFrame(options: TransposeTransformerOptions, data: DataFram const values = frame.fields.map((field) => { if (fieldType === FieldType.string) { - return fieldValuesAsStrings(field, [field.values[index - 1]])[0]; + return fieldValuesAsStrings(field, [field.values[index - 1]], emptyValue)[0]; } return field.values[index - 1]; }); @@ -97,17 +100,17 @@ function determineFieldType(fieldTypes: FieldType[]): FieldType { return uniqueFieldTypes.size === 1 ? [...uniqueFieldTypes][0] : FieldType.string; } -function fieldValuesAsStrings(field: Field, values: unknown[]) { +function fieldValuesAsStrings(field: Field, values: unknown[], emptyValue: SpecialValue) { switch (field.type) { case FieldType.time: case FieldType.number: case FieldType.boolean: case FieldType.string: - return values.map((v) => `${v}`); + return values.map((v) => (v != null ? `${v}` : getSpecialValue(emptyValue))); case FieldType.enum: // @ts-ignore - return values.map((v) => field.config.type!.enum!.text![v]); + return values.map((v) => field.config.type!.enum!.text![v] ?? getSpecialValue(emptyValue)); default: - return values.map((v) => JSON.stringify(v)); + return values.map((v) => (v != null ? JSON.stringify(v) : getSpecialValue(emptyValue))); } } diff --git a/packages/grafana-data/src/transformations/transformers/utils.ts b/packages/grafana-data/src/transformations/transformers/utils.ts index c10b1e8a1d7..29ac89b29ee 100644 --- a/packages/grafana-data/src/transformations/transformers/utils.ts +++ b/packages/grafana-data/src/transformations/transformers/utils.ts @@ -1,5 +1,6 @@ import { BootData } from '../../types/config'; import { DataFrame } from '../../types/dataFrame'; +import { SpecialValue } from '../../types/transformations'; declare global { interface Window { @@ -22,3 +23,19 @@ export function findMaxFields(data: DataFrame[]) { return maxFields; } + +export function getSpecialValue(specialValue: SpecialValue) { + switch (specialValue) { + case SpecialValue.False: + return false; + case SpecialValue.True: + return true; + case SpecialValue.Null: + return null; + case SpecialValue.Zero: + return 0; + case SpecialValue.Empty: + default: + return ''; + } +} diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 87a3dee8d06..86faf2d7f04 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -286,6 +286,10 @@ export interface FeatureToggles { */ kubernetesDashboards?: boolean; /** + * Routes short url requests from /api to the /apis endpoint + */ + kubernetesShortURLs?: boolean; + /** * Disable schema validation for dashboards/v1 */ dashboardDisableSchemaValidationV1?: boolean; @@ -1091,4 +1095,8 @@ export interface FeatureToggles { * Enable adhoc filter buttons in visualization tooltips */ adhocFiltersInTooltips?: boolean; + /** + * New Log Context component + */ + newLogContext?: boolean; } diff --git a/packages/grafana-data/src/types/icon.ts b/packages/grafana-data/src/types/icon.ts index 0cb4a61a25b..34680fc07d1 100644 --- a/packages/grafana-data/src/types/icon.ts +++ b/packages/grafana-data/src/types/icon.ts @@ -120,6 +120,8 @@ export const availableIconsIndex = { 'file-export': true, 'file-landscape-alt': true, filter: true, + 'filter-plus': true, + 'filter-minus': true, flip: true, folder: true, font: true, diff --git a/packages/grafana-data/src/types/pluginExtensions.ts b/packages/grafana-data/src/types/pluginExtensions.ts index 32764409a8d..488eced154a 100644 --- a/packages/grafana-data/src/types/pluginExtensions.ts +++ b/packages/grafana-data/src/types/pluginExtensions.ts @@ -250,6 +250,7 @@ export type PluginExtensionCommandPaletteContext = {}; export type PluginExtensionResourceAttributesContext = { // Key-value pairs of resource attributes, attribute name is the key attributes: Record; + spanAttributes?: Record; datasource: { type: string; uid: string; diff --git a/packages/grafana-data/src/utils/datasource.test.ts b/packages/grafana-data/src/utils/datasource.test.ts new file mode 100644 index 00000000000..38484a69387 --- /dev/null +++ b/packages/grafana-data/src/utils/datasource.test.ts @@ -0,0 +1,22 @@ +import { isDataSourceRef } from './datasource'; + +describe('isDataSourceRef', () => { + it('returns true for DataSourceRef with only a uid', () => { + const ref = { uid: '123', type: 'prometheus' }; + expect(isDataSourceRef(ref)).toBe(true); + }); + + it('returns true for DataSourceRef with only a type', () => { + const ref = { type: 'prometheus' }; + expect(isDataSourceRef(ref)).toBe(true); + }); + + it('returns true for DataSourceRef with both a uid and a type', () => { + const ref = { uid: '123', type: 'prometheus' }; + expect(isDataSourceRef(ref)).toBe(true); + }); + + it.each(['prometheus', null, undefined, {}, 123])('returns false for %s', (input) => { + expect(isDataSourceRef(input)).toBe(false); + }); +}); diff --git a/packages/grafana-data/src/utils/datasource.ts b/packages/grafana-data/src/utils/datasource.ts index 0f985939949..6504599e3a8 100644 --- a/packages/grafana-data/src/utils/datasource.ts +++ b/packages/grafana-data/src/utils/datasource.ts @@ -29,8 +29,14 @@ export function getDataSourceRef(ds: DataSourceInstanceSettings): DataSourceRef * * @public */ -export function isDataSourceRef(ref: DataSourceRef | string | null | undefined): ref is DataSourceRef { - return typeof ref === 'object' && typeof ref?.uid === 'string'; +export function isDataSourceRef(ref: unknown): ref is DataSourceRef { + if (typeof ref !== 'object' || ref === null) { + return false; + } + + const hasUid = 'uid' in ref && typeof ref.uid === 'string'; + const hasType = 'type' in ref && typeof ref.type === 'string'; + return hasUid || hasType; } /** diff --git a/packages/grafana-e2e-selectors/package.json b/packages/grafana-e2e-selectors/package.json index 2e5dd9f65a4..bc25b3d1bb3 100644 --- a/packages/grafana-e2e-selectors/package.json +++ b/packages/grafana-e2e-selectors/package.json @@ -52,6 +52,6 @@ "@grafana/tsconfig": "^2.0.0", "semver": "^7.7.0", "tslib": "2.8.1", - "typescript": "5.8.3" + "typescript": "5.9.2" } } diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index adf4f2efbfe..9ac23a5b24c 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -1239,6 +1239,9 @@ export const versionedComponents = { title: { [MIN_GRAFANA_VERSION]: (title: string) => `data-testid dashboard-row-title-${title}`, }, + wrapper: { + '12.1.0': (title: string) => `data-testid dashboard-row-wrapper-for-${title}`, + }, }, UserProfile: { profileSaveButton: { diff --git a/packages/grafana-flamegraph/package.json b/packages/grafana-flamegraph/package.json index 3152966e799..7132e2cd4cb 100644 --- a/packages/grafana-flamegraph/package.json +++ b/packages/grafana-flamegraph/package.json @@ -61,7 +61,7 @@ "@babel/preset-react": "7.27.1", "@grafana/tsconfig": "^2.0.0", "@rollup/plugin-node-resolve": "16.0.1", - "@testing-library/dom": "10.4.0", + "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "^6.1.2", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", @@ -81,7 +81,7 @@ "rollup-plugin-node-externals": "^8.0.0", "ts-jest": "29.4.0", "ts-node": "10.9.2", - "typescript": "5.8.3" + "typescript": "5.9.2" }, "peerDependencies": { "react": "^18.0.0", diff --git a/packages/grafana-i18n/package.json b/packages/grafana-i18n/package.json index 6ffd95918f4..036a63834e4 100644 --- a/packages/grafana-i18n/package.json +++ b/packages/grafana-i18n/package.json @@ -67,7 +67,7 @@ "@types/react": "18.3.18", "rollup": "^4.22.4", "rollup-plugin-copy": "3.5.0", - "typescript": "5.8.3" + "typescript": "5.9.2" }, "peerDependencies": { "react": ">=18" diff --git a/packages/grafana-o11y-ds-frontend/package.json b/packages/grafana-o11y-ds-frontend/package.json index 11ef5bdce98..aee2372aa14 100644 --- a/packages/grafana-o11y-ds-frontend/package.json +++ b/packages/grafana-o11y-ds-frontend/package.json @@ -31,7 +31,7 @@ }, "devDependencies": { "@grafana/tsconfig": "^2.0.0", - "@testing-library/dom": "10.4.0", + "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "^6.1.2", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", @@ -43,7 +43,7 @@ "react": "18.3.1", "ts-jest": "29.4.0", "ts-node": "10.9.2", - "typescript": "5.8.3" + "typescript": "5.9.2" }, "peerDependencies": { "react": "^18.0.0", diff --git a/packages/grafana-plugin-configs/package.json b/packages/grafana-plugin-configs/package.json index a2edd65dd30..c22258285c0 100644 --- a/packages/grafana-plugin-configs/package.json +++ b/packages/grafana-plugin-configs/package.json @@ -14,7 +14,7 @@ "@swc/jest": "^0.2.26", "@types/eslint": "9.6.1", "@types/webpack-bundle-analyzer": "^4.7.0", - "copy-webpack-plugin": "12.0.2", + "copy-webpack-plugin": "13.0.0", "eslint": "9.32.0", "eslint-webpack-plugin": "4.2.0", "fork-ts-checker-webpack-plugin": "9.1.0", @@ -25,7 +25,7 @@ "jest-environment-jsdom": "29.7.0", "replace-in-file-webpack-plugin": "1.0.6", "swc-loader": "0.2.6", - "typescript": "5.8.3", + "typescript": "5.9.2", "webpack": "5.101.0", "webpack-bundle-analyzer": "^4.10.2", "webpack-virtual-modules": "^0.6.2" diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json index b75ebc36f99..8736e3a0ef5 100644 --- a/packages/grafana-prometheus/package.json +++ b/packages/grafana-prometheus/package.json @@ -83,7 +83,7 @@ "@rollup/plugin-image": "3.0.3", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-node-resolve": "16.0.1", - "@testing-library/jest-dom": "6.6.3", + "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", "@types/jest": "29.5.14", @@ -102,7 +102,7 @@ "rollup-plugin-esbuild": "6.2.1", "rollup-plugin-node-externals": "^8.0.0", "testing-library-selector": "0.3.1", - "typescript": "5.8.3" + "typescript": "5.9.2" }, "peerDependencies": { "react": "^18.0.0", diff --git a/packages/grafana-prometheus/src/components/VariableQueryEditor.test.tsx b/packages/grafana-prometheus/src/components/VariableQueryEditor.test.tsx index 4e4c63352f3..1edbf5ba744 100644 --- a/packages/grafana-prometheus/src/components/VariableQueryEditor.test.tsx +++ b/packages/grafana-prometheus/src/components/VariableQueryEditor.test.tsx @@ -3,7 +3,6 @@ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { select } from 'react-select-event'; -import { dateTime, TimeRange } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { PrometheusDatasource } from '../datasource'; @@ -376,24 +375,4 @@ describe('PromVariableQueryEditor', () => { qryType: 5, }); }); - - test('Calls language provider with the time range received in props', async () => { - const now = dateTime('2023-09-16T21:26:00Z'); - const range: TimeRange = { - from: dateTime(now).subtract(2, 'days'), - to: now, - raw: { - from: 'now-2d', - to: 'now', - }, - }; - props.range = range; - - const languageProviderStartMock = jest.fn(); - props.datasource.languageProvider.start = languageProviderStartMock; - - render(); - - expect(languageProviderStartMock).toHaveBeenCalledWith(range); - }); }); diff --git a/packages/grafana-prometheus/src/components/VariableQueryEditor.tsx b/packages/grafana-prometheus/src/components/VariableQueryEditor.tsx index d8f99a7bc48..2ec81339219 100644 --- a/packages/grafana-prometheus/src/components/VariableQueryEditor.tsx +++ b/packages/grafana-prometheus/src/components/VariableQueryEditor.tsx @@ -77,11 +77,6 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }: // label filters have been added as a filter for metrics in label values query type const [labelFilters, setLabelFilters] = useState([]); - useEffect(() => { - datasource.languageProvider.start(range); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - useEffect(() => { if (!query) { return; diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx index e37f9b47f57..69119d2de92 100644 --- a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx +++ b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx @@ -48,6 +48,7 @@ const options: monacoTypes.editor.IStandaloneEditorConstructionOptions = { suggest: getSuggestOptions(), suggestFontSize: 12, wordWrap: 'on', + quickSuggestionsDelay: 250, }; // this number was chosen by testing various values. it might be necessary diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/monaco-completion-provider.test.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/monaco-completion-provider.test.ts new file mode 100644 index 00000000000..e4d122d8a18 --- /dev/null +++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/monaco-completion-provider.test.ts @@ -0,0 +1,367 @@ +import { dateTime, TimeRange } from '@grafana/data'; +import type { Monaco, monacoTypes } from '@grafana/ui'; + +import { DataProvider } from './data_provider'; +import { getCompletionProvider, getSuggestOptions } from './monaco-completion-provider'; + +// Mock the dependencies +jest.mock('./completions'); +jest.mock('./situation'); + +const mockGetCompletions = jest.fn(); +const mockGetSituation = jest.fn(); + +jest.mock('./completions', () => ({ + getCompletions: (...args: Parameters) => mockGetCompletions(...args), +})); + +jest.mock('./situation', () => ({ + getSituation: (...args: Parameters) => mockGetSituation(...args), +})); + +// Create proper Monaco mocks without 'any' +const createMockMonaco = (): Monaco => { + const mockRange = { + lift: jest.fn((range: monacoTypes.IRange) => range), + fromPositions: jest.fn((position: monacoTypes.Position) => ({ + startLineNumber: position.lineNumber, + endLineNumber: position.lineNumber, + startColumn: position.column, + endColumn: position.column, + })), + }; + + return { + languages: { + CompletionItemKind: { + Unit: 0, + Variable: 1, + Snippet: 2, + Enum: 3, + EnumMember: 4, + Constructor: 5, + } as const, + }, + Range: mockRange, + } as unknown as Monaco; +}; + +const createMockModel = ( + value: string, + mockWord: monacoTypes.editor.IWordAtPosition | null = null +): monacoTypes.editor.ITextModel => { + return { + getValue: () => value, + getValueInRange: jest.fn((range: monacoTypes.IRange) => { + // Convert to 0-based indexing + const startIndex = Math.max(0, range.startColumn - 1); + const endIndex = Math.min(value.length, range.endColumn - 1); + return value.substring(startIndex, endIndex); + }), + getWordAtPosition: jest.fn(() => mockWord), + getOffsetAt: jest.fn((position: monacoTypes.Position) => position.column - 1), + id: 'test-model', + } as unknown as monacoTypes.editor.ITextModel; +}; + +const createMockPosition = (column: number, lineNumber = 1): monacoTypes.Position => + ({ + column, + lineNumber, + }) as monacoTypes.Position; + +const createMockDataProvider = (): DataProvider => { + return { + monacoSettings: { + setInputInRange: jest.fn(), + suggestionsIncomplete: false, + }, + } as unknown as DataProvider; +}; + +const createMockTimeRange = (): TimeRange => ({ + from: dateTime(Date.now() - 3600000), // 1 hour ago + to: dateTime(Date.now()), + raw: { from: 'now-1h', to: 'now' }, +}); + +describe('monaco-completion-provider', () => { + let monaco: Monaco; + let dataProvider: DataProvider; + let timeRange: TimeRange; + + beforeEach(() => { + monaco = createMockMonaco(); + dataProvider = createMockDataProvider(); + timeRange = createMockTimeRange(); + + // Reset mocks + jest.clearAllMocks(); + mockGetCompletions.mockResolvedValue([]); + mockGetSituation.mockReturnValue({ type: 'METRIC_NAME' }); + + // Mock window.getSelection + Object.defineProperty(window, 'getSelection', { + writable: true, + value: jest.fn(() => ({ + toString: () => '', + })), + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe('getSuggestOptions', () => { + it('should return options with showWords set to false', () => { + const options = getSuggestOptions(); + expect(options).toEqual({ + showWords: false, + }); + }); + }); + + describe('getCompletionProvider', () => { + it('should return provider and state objects', () => { + const result = getCompletionProvider(monaco, dataProvider, timeRange); + + expect(result).toHaveProperty('provider'); + expect(result).toHaveProperty('state'); + expect(result.state).toHaveProperty('isManualTriggerRequested', false); + expect(result.provider).toHaveProperty('triggerCharacters'); + expect(result.provider).toHaveProperty('provideCompletionItems'); + }); + + it('should have correct trigger characters', () => { + const { provider } = getCompletionProvider(monaco, dataProvider, timeRange); + + expect(provider.triggerCharacters).toEqual(['{', ',', '[', '(', '=', '~', ' ', '"']); + }); + }); + + describe('provideCompletionItems', () => { + it('should return empty suggestions when no situation is detected', async () => { + mockGetSituation.mockReturnValue(null); + + const { provider } = getCompletionProvider(monaco, dataProvider, timeRange); + const model = createMockModel('test'); + const position = createMockPosition(4); + + const result = await (provider.provideCompletionItems as Function)(model, position); + + expect(result).toEqual({ + suggestions: [], + incomplete: false, + }); + }); + + it('should call getCompletions with correct parameters for normal word', async () => { + const mockWord = { word: 'grafana', startColumn: 1, endColumn: 7 }; + const model = createMockModel('grafana', mockWord); + const position = createMockPosition(7); + + const { provider } = getCompletionProvider(monaco, dataProvider, timeRange); + + await (provider.provideCompletionItems as Function)(model, position); + + expect(mockGetCompletions).toHaveBeenCalledWith( + { type: 'METRIC_NAME' }, + dataProvider, + timeRange, + 'grafana', + 'full' // Should be 'full' because word length >= 3 + ); + }); + + it('should use partial trigger type for short words', async () => { + const mockWord = { word: 'go', startColumn: 1, endColumn: 3 }; + const model = createMockModel('go', mockWord); + const position = createMockPosition(3); + + const { provider } = getCompletionProvider(monaco, dataProvider, timeRange); + + await (provider.provideCompletionItems as Function)(model, position); + + expect(mockGetCompletions).toHaveBeenCalledWith( + { type: 'METRIC_NAME' }, + dataProvider, + timeRange, + 'go', + 'partial' // Should be 'partial' because word length < 3 + ); + }); + + it('should format completion items correctly', async () => { + const mockCompletions = [ + { + label: 'test_metric', + detail: 'A test metric', + insertText: 'test_metric', + documentation: 'Test documentation', + insertTextRules: undefined, + type: 'METRIC_NAME' as const, + triggerOnInsert: false, + }, + ]; + + mockGetCompletions.mockResolvedValue(mockCompletions); + + const mockWord = { word: 'test', startColumn: 1, endColumn: 5 }; + const model = createMockModel('test', mockWord); + const position = createMockPosition(5); + + const { provider } = getCompletionProvider(monaco, dataProvider, timeRange); + + const result = await (provider.provideCompletionItems as Function)(model, position); + + expect(result?.suggestions).toHaveLength(1); + expect(result?.suggestions?.[0]).toMatchObject({ + label: 'test_metric', + detail: 'A test metric', + insertText: 'test_metric', + documentation: 'Test documentation', + kind: 5, // Constructor kind for METRIC_NAME + sortText: '0', + command: undefined, + }); + }); + + it('should add trigger command for items with triggerOnInsert', async () => { + const mockCompletions = [ + { + label: 'func(', + insertText: 'func(', + type: 'FUNCTION' as const, + triggerOnInsert: true, + }, + ]; + + mockGetCompletions.mockResolvedValue(mockCompletions); + + const model = createMockModel('func'); + const position = createMockPosition(4); + + const { provider } = getCompletionProvider(monaco, dataProvider, timeRange); + + const result = await (provider.provideCompletionItems as Function)(model, position); + + expect(result?.suggestions?.[0]?.command).toEqual({ + id: 'editor.action.triggerSuggest', + title: '', + }); + }); + }); + + describe('manual trigger handling', () => { + it('should use full trigger type for manual trigger', async () => { + const mockWord = { word: 'te', startColumn: 1, endColumn: 3 }; + const model = createMockModel('te', mockWord); + const position = createMockPosition(3); + + const { provider, state } = getCompletionProvider(monaco, dataProvider, timeRange); + + // Set manual trigger flag + state.isManualTriggerRequested = true; + + await (provider.provideCompletionItems as Function)(model, position); + + expect(mockGetCompletions).toHaveBeenCalledWith( + { type: 'METRIC_NAME' }, + dataProvider, + timeRange, + 'te', + 'full' // Should be 'full' despite short word length + ); + }); + }); + + describe('trigger character handling', () => { + const triggerCharacters = ['{', ',', '[', '(', '=', '~', ' ', '"']; + + triggerCharacters.forEach((triggerChar) => { + it(`should use full trigger type for trigger character "${triggerChar}"`, async () => { + const testString = `grafana${triggerChar}`; + const model = createMockModel(testString, null); + const position = createMockPosition(testString.length + 1); // After trigger character (1-indexed) + + const { provider } = getCompletionProvider(monaco, dataProvider, timeRange); + + await (provider.provideCompletionItems as Function)(model, position); + + expect(mockGetCompletions).toHaveBeenCalledWith( + { type: 'METRIC_NAME' }, + dataProvider, + timeRange, + undefined, // No word at position after trigger char + 'full' + ); + }); + }); + + it('should handle trigger character at beginning of line', async () => { + const model = createMockModel('{', null); + const position = createMockPosition(2); // After the { character + + const { provider } = getCompletionProvider(monaco, dataProvider, timeRange); + + await (provider.provideCompletionItems as Function)(model, position); + + // Should not fail and should still call getCompletions + expect(mockGetCompletions).toHaveBeenCalled(); + }); + }); + + describe('selection handling', () => { + it('should adjust cursor position when text is selected', async () => { + // Mock selected text + Object.defineProperty(window, 'getSelection', { + writable: true, + value: jest.fn(() => ({ + toString: () => 'selected', + })), + }); + + const model = createMockModel('grafana selected'); + const position = createMockPosition(16); // End of string + + const { provider } = getCompletionProvider(monaco, dataProvider, timeRange); + + await (provider.provideCompletionItems as Function)(model, position); + + // Should call getOffsetAt with adjusted position + expect(model.getOffsetAt).toHaveBeenCalledWith({ + column: 8, // 16 - 8 (length of 'selected') + lineNumber: 1, + }); + }); + }); + + describe('data provider integration', () => { + it('should set input range on data provider', async () => { + const mockWord = { word: 'test', startColumn: 1, endColumn: 5 }; + const model = createMockModel('test', mockWord); + const position = createMockPosition(5); + + const { provider } = getCompletionProvider(monaco, dataProvider, timeRange); + + await (provider.provideCompletionItems as Function)(model, position); + + expect(dataProvider.monacoSettings.setInputInRange).toHaveBeenCalled(); + }); + + it('should return incomplete status from data provider', async () => { + dataProvider.monacoSettings.suggestionsIncomplete = true; + mockGetCompletions.mockResolvedValue([]); + + const model = createMockModel('test'); + const position = createMockPosition(4); + + const { provider } = getCompletionProvider(monaco, dataProvider, timeRange); + + const result = await (provider.provideCompletionItems as Function)(model, position); + + expect(result?.incomplete).toBe(true); + }); + }); +}); diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/monaco-completion-provider.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/monaco-completion-provider.ts index 576985fae86..e156d5119dc 100644 --- a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/monaco-completion-provider.ts +++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/monaco-completion-provider.ts @@ -9,6 +9,13 @@ import { NeverCaseError } from './util'; export type TriggerType = 'partial' | 'full'; +export type MonacoQueryFieldLocalState = { + isManualTriggerRequested: boolean; +}; + +const TRIGGER_CHARACTERS = ['{', ',', '[', '(', '=', '~', ' ', '"']; +const MIN_WORD_LENGTH_FOR_FULL_COMPLETIONS = 3; + export function getSuggestOptions(): monacoTypes.editor.ISuggestOptions { return { // monaco-editor sometimes provides suggestions automatically, i am not @@ -50,19 +57,16 @@ function getMonacoCompletionItemKind(type: CompletionType, monaco: Monaco): mona } function getTriggerType( - context: monacoTypes.languages.CompletionContext, word: monacoTypes.editor.IWordAtPosition | null, model: monacoTypes.editor.ITextModel, position: monacoTypes.Position, - isManualTrigger: boolean + state: MonacoQueryFieldLocalState ): TriggerType { - // Manual trigger (Ctrl+Space) - if (isManualTrigger) { + // Manual trigger (Ctrl+Space) - always full completions + if (state.isManualTriggerRequested) { return 'full'; } - // Trigger characters - const triggerChars = ['{', ',', '[', '(', '=', '~', ' ', '"']; const charBeforeCursor = model.getValueInRange({ startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, @@ -70,12 +74,12 @@ function getTriggerType( endColumn: position.column, }); - if (triggerChars.includes(charBeforeCursor)) { + if (TRIGGER_CHARACTERS.includes(charBeforeCursor)) { return 'full'; } - // Word length >= 3 - if (word && word.word.length >= 3) { + // For typed words of sufficient length, use full completions + if (word && word.word.length >= MIN_WORD_LENGTH_FOR_FULL_COMPLETIONS) { return 'full'; } @@ -86,20 +90,14 @@ export function getCompletionProvider( monaco: Monaco, dataProvider: DataProvider, timeRange: TimeRange -): { provider: monacoTypes.languages.CompletionItemProvider; state: { isManualTriggerRequested: boolean } } { - // Short debounce to catch rapid typing - let debounceTimer: ReturnType | null = null; - const DEBOUNCE_DELAY = 150; // Much shorter delay to catch rapid typing - - // Simple local state - const state = { +): { provider: monacoTypes.languages.CompletionItemProvider; state: MonacoQueryFieldLocalState } { + const state: MonacoQueryFieldLocalState = { isManualTriggerRequested: false, }; const provideCompletionItems = ( model: monacoTypes.editor.ITextModel, - position: monacoTypes.Position, - context: monacoTypes.languages.CompletionContext + position: monacoTypes.Position ): monacoTypes.languages.ProviderResult => { const word = model.getWordAtPosition(position); const range = @@ -112,111 +110,68 @@ export function getCompletionProvider( }) : monaco.Range.fromPositions(position); - const isManualTrigger = state.isManualTriggerRequested; - if (isManualTrigger) { - state.isManualTriggerRequested = false; + // Set input range for data provider + dataProvider.monacoSettings.setInputInRange(model.getValueInRange(range)); + + // Get adjusted position for cursor/selection handling + const adjustedPosition = getAdjustedPosition(position); + const offset = model.getOffsetAt(adjustedPosition); + const situation = getSituation(model.getValue(), offset); + + // Early exit if no situation detected + if (situation === null) { + return Promise.resolve({ suggestions: [], incomplete: false }); } - const triggerType: TriggerType = getTriggerType(context, word, model, position, isManualTrigger); + const triggerType: TriggerType = getTriggerType(word, model, position, state); - // For immediate triggers (manual, trigger chars, or already 3+ chars), execute immediately - const isImmediate = isManualTrigger || triggerType === 'full'; + return getCompletions(situation, dataProvider, timeRange, word?.word, triggerType).then((items) => { + // Monaco by-default alphabetically orders the items. + // We use a number-as-string sortkey to maintain our custom order + const maxIndexDigits = items.length > 0 ? items.length.toString().length : 1; + const suggestions: monacoTypes.languages.CompletionItem[] = items.map((item, index) => ({ + kind: getMonacoCompletionItemKind(item.type, monaco), + label: item.label, + insertText: item.insertText, + insertTextRules: item.insertTextRules, + detail: item.detail, + documentation: item.documentation, + sortText: index.toString().padStart(maxIndexDigits, '0'), // to force the order we have + range, + command: item.triggerOnInsert + ? { + id: 'editor.action.triggerSuggest', + title: '', + } + : undefined, + })); - if (isImmediate) { - if (debounceTimer) { - clearTimeout(debounceTimer); - debounceTimer = null; - } - return executeCompletionLogic(model, position, range, dataProvider, timeRange, word?.word, triggerType); - } - - // For typing scenarios, use short debounce to catch rapid typing - if (debounceTimer) { - clearTimeout(debounceTimer); - } - - return new Promise((resolve) => { - debounceTimer = setTimeout(() => { - // Re-check if we should use full completions after debounce - const updatedWord = model.getWordAtPosition(position); - const updatedTriggerType: TriggerType = getTriggerType(context, updatedWord, model, position, false) - ? 'full' - : 'partial'; - - executeCompletionLogic( - model, - position, - range, - dataProvider, - timeRange, - updatedWord?.word, - updatedTriggerType - ).then(resolve); - }, DEBOUNCE_DELAY); + return { suggestions, incomplete: dataProvider.monacoSettings.suggestionsIncomplete }; }); }; - const executeCompletionLogic = async ( - model: monacoTypes.editor.ITextModel, - position: monacoTypes.Position, - range: monacoTypes.Range, - dataProvider: DataProvider, - timeRange: TimeRange, - wordText?: string, - triggerType: TriggerType = 'full' - ): Promise => { - // documentation says `position` will be "adjusted" in `getOffsetAt` - // i don't know what that means, to be sure i clone it - const positionClone = { - column: position.column, - lineNumber: position.lineNumber, - }; - - dataProvider.monacoSettings.setInputInRange(model.getValueInRange(range)); + // Helper function to handle position adjustment for selection + function getAdjustedPosition(position: monacoTypes.Position): { column: number; lineNumber: number } { + let adjustedColumn = position.column; // Check to see if the browser supports window.getSelection() if (window.getSelection) { const selectedText = window.getSelection()?.toString(); - // If the user has selected text, adjust the cursor position to be at the start of the selection, instead of the end + // If the user has selected text, adjust the cursor position to be at the start of the selection if (selectedText && selectedText.length > 0) { - positionClone.column = positionClone.column - selectedText.length; + adjustedColumn = Math.max(1, adjustedColumn - selectedText.length); } } - const offset = model.getOffsetAt(positionClone); - const situation = getSituation(model.getValue(), offset); - const completionsPromise = - situation != null - ? getCompletions(situation, dataProvider, timeRange, wordText, triggerType) - : Promise.resolve([]); - - return completionsPromise.then((items) => { - // monaco by-default alphabetically orders the items. - // to stop it, we use a number-as-string sortkey, - // so that monaco keeps the order we use - const maxIndexDigits = items.length.toString().length; - const suggestions: monacoTypes.languages.CompletionItem[] = items.map((item, index) => ({ - range, - label: item.label, - detail: item.detail, - insertText: item.insertText, - documentation: item.documentation, - insertTextRules: item.insertTextRules, - kind: getMonacoCompletionItemKind(item.type, monaco), - sortText: index.toString().padStart(maxIndexDigits, '0'), // to force the order we have - command: item.triggerOnInsert ? { id: 'editor.action.triggerSuggest', title: '' } : undefined, - })); - - return { - suggestions, - incomplete: dataProvider.monacoSettings.suggestionsIncomplete, - }; - }); - }; + return { + column: adjustedColumn, + lineNumber: position.lineNumber, + }; + } return { provider: { - triggerCharacters: ['{', ',', '[', '(', '=', '~', ' ', '"'], + triggerCharacters: TRIGGER_CHARACTERS, provideCompletionItems, }, state, diff --git a/packages/grafana-prometheus/src/datasource.test.ts b/packages/grafana-prometheus/src/datasource.test.ts index c5fc15d6a8f..73964477bf3 100644 --- a/packages/grafana-prometheus/src/datasource.test.ts +++ b/packages/grafana-prometheus/src/datasource.test.ts @@ -19,6 +19,7 @@ import { config, getBackendSrv, setBackendSrv, TemplateSrv } from '@grafana/runt import { extractResourceMatcher, extractRuleMappingFromGroups, PrometheusDatasource } from './datasource'; import { prometheusRegularEscape, prometheusSpecialRegexEscape } from './escaping'; import { PrometheusLanguageProviderInterface } from './language_provider'; +import { CacheRequestInfo } from './querycache/QueryCache'; import { createDataRequest, createDefaultPromResponse, @@ -1253,3 +1254,72 @@ describe('modifyQuery', () => { }); }); }); + +describe('PrometheusDatasource incremental query logic', () => { + let ds: PrometheusDatasource; + let mockCache: { + requestInfo: jest.MockedFunction<(request: DataQueryRequest) => CacheRequestInfo>; + procFrames: jest.MockedFunction<(...args: unknown[]) => unknown[]>; + }; + + beforeEach(() => { + jest.clearAllMocks(); + + mockCache = { + requestInfo: jest.fn().mockReturnValue({ + requests: [{ targets: [], range: getMockTimeRange() }], + targetSignatures: new Map(), + shouldCache: true, + }), + procFrames: jest.fn().mockReturnValue([]), + }; + + const incrementalInstanceSettings = { + url: 'proxied', + id: 1, + uid: 'ABCDEF', + access: 'proxy', + user: 'test', + password: 'mupp', + jsonData: { + customQueryParameters: '', + cacheLevel: PrometheusCacheLevel.Low, + incrementalQuerying: true, + } as Partial, + } as unknown as DataSourceInstanceSettings; + + ds = new PrometheusDatasource(incrementalInstanceSettings, templateSrvStub); + ds.cache = mockCache as unknown as typeof ds.cache; + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should use incremental query for normal queries when incrementalQuerying is true', async () => { + const request = createDataRequest([{ expr: 'up', refId: 'A' }]); + await lastValueFrom(ds.query(request)); + expect(mockCache.requestInfo).toHaveBeenCalled(); + }); + + it('should disable incremental query when query contains $__range', async () => { + const request = createDataRequest([{ expr: 'rate(up[$__range])', refId: 'A' }]); + await lastValueFrom(ds.query(request)); + expect(mockCache.requestInfo).not.toHaveBeenCalled(); + }); + + it('should disable incremental query when any target contains $__range', async () => { + const request = createDataRequest([ + { expr: 'up', refId: 'A' }, + { expr: 'rate(cpu[$__range])', refId: 'B' }, + ]); + await lastValueFrom(ds.query(request)); + expect(mockCache.requestInfo).not.toHaveBeenCalled(); + }); + + it('should disable incremental query for instant queries', async () => { + const request = createDataRequest([{ expr: 'up', refId: 'A', instant: true }]); + await lastValueFrom(ds.query(request)); + expect(mockCache.requestInfo).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/grafana-prometheus/src/datasource.ts b/packages/grafana-prometheus/src/datasource.ts index 1cc48500a80..2239b63be54 100644 --- a/packages/grafana-prometheus/src/datasource.ts +++ b/packages/grafana-prometheus/src/datasource.ts @@ -492,16 +492,16 @@ export class PrometheusDatasource return this.directAccessError(); } - let fullOrPartialRequest: DataQueryRequest; - let requestInfo: CacheRequestInfo | undefined = undefined; - const hasInstantQuery = request.targets.some((target) => target.instant); + // Use incremental query only if enabled and no instant queries or no $__range variables + const shouldUseIncrementalQuery = + this.hasIncrementalQuery && !request.targets.some((target) => target.instant || target.expr.includes('$__range')); - // Don't cache instant queries - if (this.hasIncrementalQuery && !hasInstantQuery) { + let fullOrPartialRequest: DataQueryRequest = request; + let requestInfo: CacheRequestInfo | undefined = undefined; + + if (shouldUseIncrementalQuery) { requestInfo = this.cache.requestInfo(request); fullOrPartialRequest = requestInfo.requests[0]; - } else { - fullOrPartialRequest = request; } const targets = fullOrPartialRequest.targets.map((target) => this.processTargetV2(target, fullOrPartialRequest)); diff --git a/packages/grafana-prometheus/src/locales/cs-CZ/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/cs-CZ/grafana-prometheus.json index 3b17ab6a5a8..44a8acbc7a2 100644 --- a/packages/grafana-prometheus/src/locales/cs-CZ/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/cs-CZ/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "Filtrovat názvy metrik podle vyhledávání regulárních výrazů pomocí dalšího volání rozhraní Prometheus API.", - "disable-text-wrap": "Zakázat obtékání textu" - }, "feedback-link": { "give-feedback": "Poskytnout zpětnou vazbu", "title-give-feedback": "Průzkumník metrik je nový. Dejte nám prosím vědět, jak ho můžeme vylepšit" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,18 +371,10 @@ "tooltip-metric": "Volitelné: vrátí seznam hodnot štítků pro název štítku v zadané metrice." }, "metrics-modal": { - "additional-settings": "Dodatečná nastavení", - "aria-label-additional-settings": "Další nastavení", "aria-label-browse-metrics": "Procházet metriky", "currently-selected": "Aktuálně vybráno: {{selected}}", "metrics-pre-filtered": "Tyto metriky byly předem filtrovány štítky vybranými ve filtrech štítků.", - "placeholder-results-per-page": "výsledků na stránku", - "results-per-page": "Výsledky na stránku", - "title-metrics-explorer": "Průzkumník metrik", - "results-amount_one": "Zobrazuje se {{num}} z {{count}} výsledků", - "results-amount_few": "Zobrazuje se {{num}} z {{count}} výsledků", - "results-amount_many": "Zobrazuje se {{num}} z {{count}} výsledků", - "results-amount_other": "Zobrazuje se {{num}} z {{count}} výsledků" + "title-metrics-explorer": "Průzkumník metrik" }, "nested-query": { "label": { @@ -502,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Název", - "select": "Vybrat", "type": "Typ" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/de-DE/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/de-DE/grafana-prometheus.json index 7b2db63bb8b..c391ba11f3d 100644 --- a/packages/grafana-prometheus/src/locales/de-DE/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/de-DE/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "Filtern Sie Metriknamen per Regex-Suche und nutzen Sie dabei einen zusätzlichen Aufruf der Prometheus-API.", - "disable-text-wrap": "Textumbruch deaktivieren" - }, "feedback-link": { "give-feedback": "Feedback geben", "title-give-feedback": "Der Metrik-Explorer ist neu. Bitte teilen Sie uns mit, wie wir ihn verbessern können." @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,16 +371,10 @@ "tooltip-metric": "Optional: gibt eine Liste von Label-Werten für den Label-Namen in der angegebenen Metrik zurück." }, "metrics-modal": { - "additional-settings": "Zusätzliche Einstellungen", - "aria-label-additional-settings": "Zusätzliche Einstellungen", "aria-label-browse-metrics": "Metriken durchsuchen", "currently-selected": "Aktuell ausgewählt: {{selected}}", "metrics-pre-filtered": "Diese Metriken wurden anhand der Labels, die in den Label-Filtern ausgewählt wurden, vorgefiltert.", - "placeholder-results-per-page": "Ergebnisse pro Seite", - "results-per-page": "Ergebnisse pro Seite", - "title-metrics-explorer": "Metrik-Explorer", - "results-amount_one": "{{num}} von {{count}} Ergebnissen werden angezeigt", - "results-amount_other": "{{num}} von {{count}} Ergebnissen werden angezeigt" + "title-metrics-explorer": "Metrik-Explorer" }, "nested-query": { "label": { @@ -500,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Name", - "select": "Auswählen", "type": "Typ" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json index b94bb4cded3..f67ac0e3e9f 100644 --- a/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "Filter metric names by regex search, using an additional call on the Prometheus API.", - "disable-text-wrap": "Disable text wrap" - }, "feedback-link": { "give-feedback": "Give feedback", "title-give-feedback": "The metrics explorer is new, please let us know how we can improve it" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "Search metrics by name", - "include-null-metadata": "Include results with no metadata", - "metadata-search-switch": "Include description in search", - "set-use-backend": "Enable regex search", "type": "Filter by type" }, "get-prom-types": { @@ -378,15 +371,9 @@ "tooltip-metric": "Optional: returns a list of label values for the label name in the specified metric." }, "metrics-modal": { - "additional-settings": "Additional Settings", - "aria-label-additional-settings": "Additional settings", "aria-label-browse-metrics": "Browse metrics", "currently-selected": "Currently selected: {{selected}}", "metrics-pre-filtered": "These metrics have been pre-filtered by labels chosen in the label filters.", - "placeholder-results-per-page": "results per page", - "results-amount_one": "Showing {{num}} of {{count}} results", - "results-amount_other": "Showing {{num}} of {{count}} results", - "results-per-page": "Results per page", "title-metrics-explorer": "Metrics explorer" }, "nested-query": { @@ -500,7 +487,6 @@ "message-expand-search": "There are no metrics found. Try to expand your search and filters.", "message-no-metrics-found": "There are no metrics found in the data source.", "name": "Name", - "select": "Select", "type": "Type" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/es-ES/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/es-ES/grafana-prometheus.json index 8feaf63282c..c0016de4b4a 100644 --- a/packages/grafana-prometheus/src/locales/es-ES/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/es-ES/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "", - "disable-text-wrap": "" - }, "feedback-link": { "give-feedback": "Enviar comentarios", "title-give-feedback": "" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,16 +371,10 @@ "tooltip-metric": "" }, "metrics-modal": { - "additional-settings": "", - "aria-label-additional-settings": "Configuraciones adicionales", "aria-label-browse-metrics": "", "currently-selected": "", "metrics-pre-filtered": "", - "placeholder-results-per-page": "", - "results-per-page": "", - "title-metrics-explorer": "", - "results-amount_one": "", - "results-amount_other": "" + "title-metrics-explorer": "" }, "nested-query": { "label": { @@ -500,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Nombre", - "select": "Seleccionar", "type": "Tipo" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/fr-FR/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/fr-FR/grafana-prometheus.json index 1b48f0ac903..75fffbffa7e 100644 --- a/packages/grafana-prometheus/src/locales/fr-FR/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/fr-FR/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "Filtrez les noms de métriques par recherche d’expression régulière, en utilisant un appel supplémentaire sur l’API Prometheus.", - "disable-text-wrap": "Désactiver le retour à la ligne automatique" - }, "feedback-link": { "give-feedback": "Publiez votre commentaire", "title-give-feedback": "L’explorateur de métriques est une nouvelle fonctionnalité ; faites-nous part de vos retours pour l’améliorer" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,16 +371,10 @@ "tooltip-metric": "Facultatif : renvoie une liste de valeurs d’étiquette pour le nom d’étiquette dans la métrique spécifiée." }, "metrics-modal": { - "additional-settings": "Paramètres additionnels", - "aria-label-additional-settings": "Paramètres supplémentaires", "aria-label-browse-metrics": "Parcourir les métriques", "currently-selected": "Sélection actuelle : {{selected}}", "metrics-pre-filtered": "Ces métriques ont été pré-filtrées par les étiquettes choisies dans les filtres d’étiquettes.", - "placeholder-results-per-page": "résultats par page", - "results-per-page": "Résultats par page", - "title-metrics-explorer": "Explorateur de métriques", - "results-amount_one": "Affichage de {{num}} résultats sur {{count}}", - "results-amount_other": "Affichage de {{num}} résultats sur {{count}}" + "title-metrics-explorer": "Explorateur de métriques" }, "nested-query": { "label": { @@ -500,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Nom", - "select": "Sélectionner", "type": "Type" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/hu-HU/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/hu-HU/grafana-prometheus.json index 2b4464c68f6..454e51f3684 100644 --- a/packages/grafana-prometheus/src/locales/hu-HU/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/hu-HU/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "A metrikanevek szűrése reguláris kifejezéses kereséssel, a Prometheus API további hívásával.", - "disable-text-wrap": "Sortörés letiltása" - }, "feedback-link": { "give-feedback": "Visszajelzés küldése", "title-give-feedback": "A metrikaböngésző új, és számítunk az építő jellegű visszajelzésére" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,16 +371,10 @@ "tooltip-metric": "Opcionális: a megadott metrikában a címke nevéhez tartozó címkeértékek listáját adja vissza." }, "metrics-modal": { - "additional-settings": "További beállítások", - "aria-label-additional-settings": "További beállítások", "aria-label-browse-metrics": "Metrikák böngészése", "currently-selected": "Jelenleg kiválasztott: {{selected}}", "metrics-pre-filtered": "Ezek a metrikák előszűrtek a címkeszűrőkben kiválasztott címkék alapján.", - "placeholder-results-per-page": "találat oldalanként", - "results-per-page": "Találatok száma oldalanként", - "title-metrics-explorer": "Metrikaböngésző", - "results-amount_one": "{{count}}/{{num}} találat megjelenítése", - "results-amount_other": "{{count}}/{{num}} találat megjelenítése" + "title-metrics-explorer": "Metrikaböngésző" }, "nested-query": { "label": { @@ -500,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Név", - "select": "Kijelölés", "type": "Típus" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/id-ID/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/id-ID/grafana-prometheus.json index bbcd6d32f7e..e674183019f 100644 --- a/packages/grafana-prometheus/src/locales/id-ID/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/id-ID/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "Filter nama metrik dengan pencarian regex, menggunakan panggilan tambahan pada API Prometheus.", - "disable-text-wrap": "Nonaktifkan pemenggalan teks otomatis" - }, "feedback-link": { "give-feedback": "Berikan umpan balik", "title-give-feedback": "Penjelajah metrik ini masih baru, beri tahu kami cara untuk meningkatkannya" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,15 +371,10 @@ "tooltip-metric": "Opsional: mengembalikan daftar nilai label untuk nama label dalam metrik yang ditentukan." }, "metrics-modal": { - "additional-settings": "Pengaturan Tambahan", - "aria-label-additional-settings": "Pengaturan tambahan", "aria-label-browse-metrics": "Telusuri metrik", "currently-selected": "Sedang dipilih: {{selected}}", "metrics-pre-filtered": "Metrik ini telah difilter sebelumnya berdasarkan label yang dipilih di filter label.", - "placeholder-results-per-page": "hasil per halaman", - "results-per-page": "Hasil per halaman", - "title-metrics-explorer": "Penjelajah metrik", - "results-amount_other": "Menampilkan {{num}} dari {{count}} hasil" + "title-metrics-explorer": "Penjelajah metrik" }, "nested-query": { "label": { @@ -499,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Nama", - "select": "Pilih", "type": "Jenis" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/it-IT/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/it-IT/grafana-prometheus.json index 9cfc70e6c88..2671478ca0f 100644 --- a/packages/grafana-prometheus/src/locales/it-IT/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/it-IT/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "", - "disable-text-wrap": "" - }, "feedback-link": { "give-feedback": "Lascia un feedback", "title-give-feedback": "" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,16 +371,10 @@ "tooltip-metric": "" }, "metrics-modal": { - "additional-settings": "", - "aria-label-additional-settings": "Impostazioni aggiuntive", "aria-label-browse-metrics": "", "currently-selected": "", "metrics-pre-filtered": "", - "placeholder-results-per-page": "", - "results-per-page": "", - "title-metrics-explorer": "", - "results-amount_one": "", - "results-amount_other": "" + "title-metrics-explorer": "" }, "nested-query": { "label": { @@ -500,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Nome", - "select": "Seleziona", "type": "Tipo" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/ja-JP/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/ja-JP/grafana-prometheus.json index c4f13c7d462..72b1785b222 100644 --- a/packages/grafana-prometheus/src/locales/ja-JP/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/ja-JP/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "Prometheus APIの追加呼び出しを使用して、正規表現検索でメトリック名をフィルタリングします。", - "disable-text-wrap": "テキストの折り返しを無効化" - }, "feedback-link": { "give-feedback": "フィードバックを送信", "title-give-feedback": "メトリックエクスプローラーは新機能です。改善点についてお聞かせください" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,15 +371,10 @@ "tooltip-metric": "オプション: 指定されたメトリックのラベル名に対するラベル値のリストを返します。" }, "metrics-modal": { - "additional-settings": "追加設定", - "aria-label-additional-settings": "追加設定", "aria-label-browse-metrics": "メトリックを参照", "currently-selected": "現在選択中: {{selected}}", "metrics-pre-filtered": "これらのメトリックは、ラベルフィルターで選択されたラベルによって事前にフィルタリングされています。", - "placeholder-results-per-page": "1ページあたりの結果数", - "results-per-page": "1ページあたりの結果数", - "title-metrics-explorer": "メトリックエクスプローラー", - "results-amount_other": "{{count}}件中{{num}}件の結果を表示" + "title-metrics-explorer": "メトリックエクスプローラー" }, "nested-query": { "label": { @@ -499,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "名前", - "select": "選択", "type": "種類" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/ko-KR/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/ko-KR/grafana-prometheus.json index f9824a261d9..975a9631f32 100644 --- a/packages/grafana-prometheus/src/locales/ko-KR/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/ko-KR/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "", - "disable-text-wrap": "" - }, "feedback-link": { "give-feedback": "피드백 제출하기", "title-give-feedback": "" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,15 +371,10 @@ "tooltip-metric": "" }, "metrics-modal": { - "additional-settings": "", - "aria-label-additional-settings": "추가 설정", "aria-label-browse-metrics": "", "currently-selected": "", "metrics-pre-filtered": "", - "placeholder-results-per-page": "", - "results-per-page": "", - "title-metrics-explorer": "", - "results-amount_other": "" + "title-metrics-explorer": "" }, "nested-query": { "label": { @@ -499,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "이름", - "select": "선택", "type": "유형" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/nl-NL/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/nl-NL/grafana-prometheus.json index 9a1280a547c..f20146043d3 100644 --- a/packages/grafana-prometheus/src/locales/nl-NL/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/nl-NL/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "Filter metrische namen op regex-zoekopdracht, met behulp van een extra oproep op de Prometheus-API.", - "disable-text-wrap": "Tekstterugloop uitschakelen" - }, "feedback-link": { "give-feedback": "Feedback geven", "title-give-feedback": "De metriekverkenner is nieuw, laat ons weten hoe we deze kunnen verbeteren" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,16 +371,10 @@ "tooltip-metric": "Optioneel: retourneert een lijst met labelwaarden voor de labelnaam in de opgegeven metriek." }, "metrics-modal": { - "additional-settings": "Extra instellingen", - "aria-label-additional-settings": "Aanvullende instellingen", "aria-label-browse-metrics": "Metriek bekijken", "currently-selected": "Momenteel geselecteerd: {{selected}}", "metrics-pre-filtered": "Deze metriek is vooraf gefilterd op labels die zijn gekozen in de labelfilters.", - "placeholder-results-per-page": "resultaten per pagina", - "results-per-page": "Resultaten per pagina", - "title-metrics-explorer": "Metriekverkenner", - "results-amount_one": "{{num}} resultaten van {{count}} weergeven", - "results-amount_other": "{{num}} resultaten van {{count}} weergeven" + "title-metrics-explorer": "Metriekverkenner" }, "nested-query": { "label": { @@ -500,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Naam", - "select": "Selecteren", "type": "Type" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/pl-PL/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/pl-PL/grafana-prometheus.json index a52ae2f32a7..fe3211ff5e4 100644 --- a/packages/grafana-prometheus/src/locales/pl-PL/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/pl-PL/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "", - "disable-text-wrap": "" - }, "feedback-link": { "give-feedback": "Przekaż opinię", "title-give-feedback": "" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,18 +371,10 @@ "tooltip-metric": "" }, "metrics-modal": { - "additional-settings": "", - "aria-label-additional-settings": "Ustawienia dodatkowe", "aria-label-browse-metrics": "", "currently-selected": "", "metrics-pre-filtered": "", - "placeholder-results-per-page": "", - "results-per-page": "", - "title-metrics-explorer": "", - "results-amount_one": "", - "results-amount_few": "", - "results-amount_many": "", - "results-amount_other": "" + "title-metrics-explorer": "" }, "nested-query": { "label": { @@ -502,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Imię", - "select": "Wybierz", "type": "Typ" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/pt-BR/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/pt-BR/grafana-prometheus.json index cf886e70733..507ddf3c792 100644 --- a/packages/grafana-prometheus/src/locales/pt-BR/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/pt-BR/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "", - "disable-text-wrap": "" - }, "feedback-link": { "give-feedback": "Dar feedback", "title-give-feedback": "" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,16 +371,10 @@ "tooltip-metric": "" }, "metrics-modal": { - "additional-settings": "", - "aria-label-additional-settings": "Configurações adicionais", "aria-label-browse-metrics": "", "currently-selected": "", "metrics-pre-filtered": "", - "placeholder-results-per-page": "", - "results-per-page": "", - "title-metrics-explorer": "", - "results-amount_one": "", - "results-amount_other": "" + "title-metrics-explorer": "" }, "nested-query": { "label": { @@ -500,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Nome", - "select": "Selecionar", "type": "Tipo" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/pt-PT/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/pt-PT/grafana-prometheus.json index b6d796a828c..3f80c60195a 100644 --- a/packages/grafana-prometheus/src/locales/pt-PT/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/pt-PT/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "", - "disable-text-wrap": "" - }, "feedback-link": { "give-feedback": "Dar feedback", "title-give-feedback": "" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,16 +371,10 @@ "tooltip-metric": "" }, "metrics-modal": { - "additional-settings": "", - "aria-label-additional-settings": "Definições adicionais", "aria-label-browse-metrics": "", "currently-selected": "", "metrics-pre-filtered": "", - "placeholder-results-per-page": "", - "results-per-page": "", - "title-metrics-explorer": "", - "results-amount_one": "", - "results-amount_other": "" + "title-metrics-explorer": "" }, "nested-query": { "label": { @@ -500,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Nome", - "select": "Selecionar", "type": "Tipo" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/ru-RU/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/ru-RU/grafana-prometheus.json index 473f1f146f7..896c67e458f 100644 --- a/packages/grafana-prometheus/src/locales/ru-RU/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/ru-RU/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "", - "disable-text-wrap": "" - }, "feedback-link": { "give-feedback": "Отправить отзыв", "title-give-feedback": "" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,18 +371,10 @@ "tooltip-metric": "" }, "metrics-modal": { - "additional-settings": "", - "aria-label-additional-settings": "Дополнительные параметры", "aria-label-browse-metrics": "", "currently-selected": "", "metrics-pre-filtered": "", - "placeholder-results-per-page": "", - "results-per-page": "", - "title-metrics-explorer": "", - "results-amount_one": "", - "results-amount_few": "", - "results-amount_many": "", - "results-amount_other": "" + "title-metrics-explorer": "" }, "nested-query": { "label": { @@ -502,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Имя", - "select": "Выбрать", "type": "Тип" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/sv-SE/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/sv-SE/grafana-prometheus.json index 017e25b5ae3..f51290dffe1 100644 --- a/packages/grafana-prometheus/src/locales/sv-SE/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/sv-SE/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "", - "disable-text-wrap": "" - }, "feedback-link": { "give-feedback": "Ge feedback", "title-give-feedback": "" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,16 +371,10 @@ "tooltip-metric": "" }, "metrics-modal": { - "additional-settings": "", - "aria-label-additional-settings": "Ytterligare inställningar", "aria-label-browse-metrics": "", "currently-selected": "", "metrics-pre-filtered": "", - "placeholder-results-per-page": "", - "results-per-page": "", - "title-metrics-explorer": "", - "results-amount_one": "", - "results-amount_other": "" + "title-metrics-explorer": "" }, "nested-query": { "label": { @@ -500,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Namn", - "select": "Välj", "type": "Typ" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/tr-TR/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/tr-TR/grafana-prometheus.json index 10e63ea4441..9aa271d54fe 100644 --- a/packages/grafana-prometheus/src/locales/tr-TR/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/tr-TR/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "", - "disable-text-wrap": "" - }, "feedback-link": { "give-feedback": "Geri bildirim gönder", "title-give-feedback": "" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,16 +371,10 @@ "tooltip-metric": "" }, "metrics-modal": { - "additional-settings": "", - "aria-label-additional-settings": "Ek ayarlar", "aria-label-browse-metrics": "", "currently-selected": "", "metrics-pre-filtered": "", - "placeholder-results-per-page": "", - "results-per-page": "", - "title-metrics-explorer": "", - "results-amount_one": "", - "results-amount_other": "" + "title-metrics-explorer": "" }, "nested-query": { "label": { @@ -500,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "Ad", - "select": "Seçin", "type": "Tür" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/zh-Hans/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/zh-Hans/grafana-prometheus.json index 4050d264fa2..f15a039030c 100644 --- a/packages/grafana-prometheus/src/locales/zh-Hans/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/zh-Hans/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "使用 Prometheus API 上的其他调用,通过正则表达式搜索筛选指标名称。", - "disable-text-wrap": "禁用文本换行" - }, "feedback-link": { "give-feedback": "提供反馈", "title-give-feedback": "指标浏览器是新功能,请告诉我们如何改进" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,15 +371,10 @@ "tooltip-metric": "可选:返回指定指标中标签名称的标签值列表。" }, "metrics-modal": { - "additional-settings": "其他设置", - "aria-label-additional-settings": "附加设置", "aria-label-browse-metrics": "浏览指标", "currently-selected": "当前选择:{{selected}}", "metrics-pre-filtered": "这些指标已通过标签筛选器中选择的标签进行预筛选。", - "placeholder-results-per-page": "每页显示结果数", - "results-per-page": "每页结果数", - "title-metrics-explorer": "指标浏览器", - "results-amount_other": "显示 {{num}} 个结果(共 {{count}} 个)" + "title-metrics-explorer": "指标浏览器" }, "nested-query": { "label": { @@ -499,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "名称", - "select": "选择", "type": "类型" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/locales/zh-Hant/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/zh-Hant/grafana-prometheus.json index 33c4d871bb0..d6a441a81bf 100644 --- a/packages/grafana-prometheus/src/locales/zh-Hant/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/zh-Hant/grafana-prometheus.json @@ -311,10 +311,6 @@ } }, "querybuilder": { - "additional-settings": { - "content-filter-metric-names-regex-search-using": "透過正規表達式搜尋篩選指標名稱,並使用額外的 Prometheus API 呼叫。", - "disable-text-wrap": "停用文字換行" - }, "feedback-link": { "give-feedback": "提供意見回饋", "title-give-feedback": "指標瀏覽器為新功能,請告訴我們如何改善" @@ -328,9 +324,6 @@ }, "get-placeholders": { "browse": "", - "include-null-metadata": "", - "metadata-search-switch": "", - "set-use-backend": "", "type": "" }, "get-prom-types": { @@ -378,15 +371,10 @@ "tooltip-metric": "選擇性設定:回傳指定指標中該標籤名稱的標籤值清單。" }, "metrics-modal": { - "additional-settings": "附加設定", - "aria-label-additional-settings": "附加設定", "aria-label-browse-metrics": "瀏覽指標", "currently-selected": "目前已選取:{{selected}}", "metrics-pre-filtered": "這些指標已透過標籤篩選條件中選擇的標籤進行預先篩選。", - "placeholder-results-per-page": "每頁顯示結果", - "results-per-page": "每頁顯示的結果", - "title-metrics-explorer": "指標瀏覽器", - "results-amount_other": "結果共 {{count}} 項,顯示 {{num}} 項" + "title-metrics-explorer": "指標瀏覽器" }, "nested-query": { "label": { @@ -499,7 +487,6 @@ "message-expand-search": "", "message-no-metrics-found": "", "name": "名稱(名字)", - "select": "選取", "type": "類型" }, "update-function-args": { diff --git a/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.tsx b/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.tsx index 21a88427159..eaafcb1aa68 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/MetricCombobox.tsx @@ -11,9 +11,8 @@ import { PrometheusDatasource } from '../../datasource'; import { QueryBuilderLabelFilter } from '../shared/types'; import { PromVisualQuery } from '../types'; +import { formatKeyValueStrings } from './formatter'; import { MetricsModal } from './metrics-modal/MetricsModal'; -import { tracking } from './metrics-modal/state/helpers'; -import { formatKeyValueStrings } from './shared/formatter'; export interface MetricComboboxProps { metricLookupDisabled: boolean; @@ -76,18 +75,6 @@ export function MetricCombobox({ [getMetricLabels, onGetMetrics] ); - const loadMetricsExplorerMetrics = useCallback(async () => { - const allMetrics = await onGetMetrics(); - const metrics: string[] = []; - for (const metric of allMetrics) { - if (metric.value) { - metrics.push(metric.value); - } - } - - return metrics; - }, [onGetMetrics]); - const asyncSelect = () => { return ( @@ -115,10 +102,7 @@ export function MetricCombobox({ )} variant="secondary" icon="book-open" - onClick={() => { - tracking('grafana_prometheus_metric_encyclopedia_open', null, '', query); - setMetricsModalOpen(true); - }} + onClick={() => setMetricsModalOpen(true)} /> ); @@ -133,7 +117,6 @@ export function MetricCombobox({ onClose={() => setMetricsModalOpen(false)} query={query} onChange={onChange} - initialMetrics={loadMetricsExplorerMetrics} timeRange={timeRange} /> )} diff --git a/packages/grafana-prometheus/src/querybuilder/components/NestedQuery.tsx b/packages/grafana-prometheus/src/querybuilder/components/NestedQuery.tsx index 8ac17b1b854..daa84a83c62 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/NestedQuery.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/NestedQuery.tsx @@ -11,7 +11,7 @@ import { PrometheusDatasource } from '../../datasource'; import { binaryScalarDefs } from '../binaryScalarOperations'; import { PromVisualQueryBinary } from '../types'; -import { QueryBuilderContent } from './shared/QueryBuilderContent'; +import { QueryBuilderContent } from './QueryBuilderContent'; interface NestedQueryProps { nestedQuery: PromVisualQueryBinary; diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx index 03bf97718bc..9504e50b5c0 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx @@ -6,7 +6,8 @@ import { PanelData } from '@grafana/data'; import { PrometheusDatasource } from '../../datasource'; import { PromVisualQuery } from '../types'; -import { BaseQueryBuilder } from './shared/BaseQueryBuilder'; +import { NestedQueryList } from './NestedQueryList'; +import { QueryBuilderContent } from './QueryBuilderContent'; interface PromQueryBuilderProps { query: PromVisualQuery; @@ -18,7 +19,22 @@ interface PromQueryBuilderProps { } export const PromQueryBuilder = memo((props) => { - return ; + const { query, datasource, onChange, onRunQuery, showExplain } = props; + + return ( + <> + + {query.binaryQueries && query.binaryQueries.length > 0 && ( + + )} + + ); }); PromQueryBuilder.displayName = 'PromQueryBuilder'; diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderContainer.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderContainer.test.tsx index b325da19d45..3e400f13a50 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderContainer.test.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderContainer.test.tsx @@ -20,12 +20,8 @@ describe('PromQueryBuilderContainer', () => { await addOperationInQueryBuilder('Range functions', 'Rate'); // extra fields here are for storing metrics explorer settings. Future work: store these in local storage. expect(props.onChange).toHaveBeenCalledWith({ - disableTextWrap: false, expr: 'rate(metric_test{job="testjob"}[$__rate_interval])', - fullMetaSearch: false, - includeNullMetadata: true, refId: 'A', - useBackend: false, }); }); diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderContainer.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderContainer.tsx index 318d4a71213..31d3b6a6274 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderContainer.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilderContainer.tsx @@ -1,6 +1,5 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilderContainer.tsx -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { useEffect, useReducer } from 'react'; +import { useEffect, useState } from 'react'; import { PanelData } from '@grafana/data'; @@ -12,7 +11,6 @@ import { PromVisualQuery } from '../types'; import { PromQueryBuilder } from './PromQueryBuilder'; import { QueryPreview } from './QueryPreview'; -import { getSettings, MetricsModalSettings } from './metrics-modal/state/state'; interface PromQueryBuilderContainerProps { query: PromQuery; @@ -23,7 +21,7 @@ interface PromQueryBuilderContainerProps { showExplain: boolean; } -interface State { +interface RenderedQuery { visQuery?: PromVisualQuery; expr: string; } @@ -33,80 +31,38 @@ interface State { */ export function PromQueryBuilderContainer(props: PromQueryBuilderContainerProps) { const { query, onChange, onRunQuery, datasource, data, showExplain } = props; - const [state, dispatch] = useReducer(stateSlice.reducer, { expr: query.expr }); - // Only rebuild visual query if expr changes from outside - useEffect(() => { - dispatch(exprChanged(query.expr)); - dispatch( - setMetricsModalSettings({ - useBackend: query.useBackend ?? false, - disableTextWrap: query.disableTextWrap ?? false, - fullMetaSearch: query.fullMetaSearch ?? false, - includeNullMetadata: query.includeNullMetadata ?? true, - }) - ); - }, [query]); + const [rendered, setRendered] = useState({ expr: query.expr }); useEffect(() => { - datasource.languageProvider.start(data?.timeRange); - }, [data?.timeRange, datasource.languageProvider]); + // Only rebuild visual query if expr changes from outside + if (!rendered.visQuery || rendered.expr !== query.expr) { + const parseResult = buildVisualQueryFromString(query.expr ?? ''); + + setRendered({ expr: query.expr, visQuery: parseResult.query }); + } + }, [query, rendered]); const onVisQueryChange = (visQuery: PromVisualQuery) => { const expr = promQueryModeller.renderQuery(visQuery); - dispatch(visualQueryChange({ visQuery, expr })); - - const metricsModalSettings = getSettings(visQuery); - onChange({ ...props.query, expr: expr, ...metricsModalSettings }); + setRendered({ expr, visQuery }); + onChange({ ...props.query, expr }); }; - if (!state.visQuery) { + if (!rendered.visQuery) { return null; } return ( <> - {} + ); } - -const initialState: State = { - expr: '', -}; - -const stateSlice = createSlice({ - name: 'prom-builder-container', - initialState, - reducers: { - visualQueryChange: (state, action: PayloadAction<{ visQuery: PromVisualQuery; expr: string }>) => { - state.expr = action.payload.expr; - state.visQuery = action.payload.visQuery; - }, - exprChanged: (state, action: PayloadAction) => { - if (!state.visQuery || state.expr !== action.payload) { - state.expr = action.payload; - const parseResult = buildVisualQueryFromString(action.payload ?? ''); - - state.visQuery = parseResult.query; - } - }, - setMetricsModalSettings: (state, action: PayloadAction) => { - if (state.visQuery) { - state.visQuery.useBackend = action.payload.useBackend; - state.visQuery.disableTextWrap = action.payload.disableTextWrap; - state.visQuery.fullMetaSearch = action.payload.fullMetaSearch; - state.visQuery.includeNullMetadata = action.payload.includeNullMetadata; - } - }, - }, -}); - -const { visualQueryChange, exprChanged, setMetricsModalSettings } = stateSlice.actions; diff --git a/packages/grafana-prometheus/src/querybuilder/components/shared/QueryBuilderContent.tsx b/packages/grafana-prometheus/src/querybuilder/components/QueryBuilderContent.tsx similarity index 67% rename from packages/grafana-prometheus/src/querybuilder/components/shared/QueryBuilderContent.tsx rename to packages/grafana-prometheus/src/querybuilder/components/QueryBuilderContent.tsx index ce00f36cbb9..b04537da808 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/shared/QueryBuilderContent.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/QueryBuilderContent.tsx @@ -1,28 +1,37 @@ import { css } from '@emotion/css'; import { memo, useState } from 'react'; -import { DataSourceApi, getDefaultTimeRange } from '@grafana/data'; +import { DataSourceApi, getDefaultTimeRange, PanelData } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { EditorRow } from '@grafana/plugin-ui'; -import { promqlGrammar } from '../../../promql'; -import { getInitHints } from '../../../query_hints'; -import { buildVisualQueryFromString } from '../../parsing'; -import { OperationExplainedBox } from '../../shared/OperationExplainedBox'; -import { OperationList } from '../../shared/OperationList'; -import { OperationListExplained } from '../../shared/OperationListExplained'; -import { OperationsEditorRow } from '../../shared/OperationsEditorRow'; -import { QueryBuilderHints } from '../../shared/QueryBuilderHints'; -import { RawQuery } from '../../shared/RawQuery'; -import { promQueryModeller } from '../../shared/modeller_instance'; -import { QueryBuilderOperation } from '../../shared/types'; -import { PromVisualQuery } from '../../types'; -import { MetricsLabelsSection } from '../MetricsLabelsSection'; -import { EXPLAIN_LABEL_FILTER_CONTENT } from '../PromQueryBuilderExplained'; +import { PrometheusDatasource } from '../../datasource'; +import { promqlGrammar } from '../../promql'; +import { getInitHints } from '../../query_hints'; +import { buildVisualQueryFromString } from '../parsing'; +import { OperationExplainedBox } from '../shared/OperationExplainedBox'; +import { OperationList } from '../shared/OperationList'; +import { OperationListExplained } from '../shared/OperationListExplained'; +import { OperationsEditorRow } from '../shared/OperationsEditorRow'; +import { QueryBuilderHints } from '../shared/QueryBuilderHints'; +import { RawQuery } from '../shared/RawQuery'; +import { promQueryModeller } from '../shared/modeller_instance'; +import { QueryBuilderOperation } from '../shared/types'; +import { PromVisualQuery } from '../types'; -import { BaseQueryBuilderProps } from './types'; +import { MetricsLabelsSection } from './MetricsLabelsSection'; +import { EXPLAIN_LABEL_FILTER_CONTENT } from './PromQueryBuilderExplained'; -export const QueryBuilderContent = memo((props) => { +interface QueryBuilderContentProps { + query: PromVisualQuery; + datasource: PrometheusDatasource; + onChange: (update: PromVisualQuery) => void; + onRunQuery: () => void; + data?: PanelData; + showExplain: boolean; +} + +export const QueryBuilderContent = memo((props) => { const { datasource, query, onChange, onRunQuery, data, showExplain } = props; const [highlightedOp, setHighlightedOp] = useState(); diff --git a/packages/grafana-prometheus/src/querybuilder/components/shared/formatter.ts b/packages/grafana-prometheus/src/querybuilder/components/formatter.ts similarity index 81% rename from packages/grafana-prometheus/src/querybuilder/components/shared/formatter.ts rename to packages/grafana-prometheus/src/querybuilder/components/formatter.ts index baf8228c573..bb795745816 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/shared/formatter.ts +++ b/packages/grafana-prometheus/src/querybuilder/components/formatter.ts @@ -1,7 +1,7 @@ -import { regexifyLabelValuesQueryString } from '../../parsingUtils'; -import { QueryBuilderLabelFilter } from '../../shared/types'; +import { regexifyLabelValuesQueryString } from '../parsingUtils'; +import { QueryBuilderLabelFilter } from '../shared/types'; -export const formatPrometheusLabelFiltersToString = ( +const formatPrometheusLabelFiltersToString = ( queryString: string, labelsFilters: QueryBuilderLabelFilter[] | undefined ): string => { diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/AdditionalSettings.tsx b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/AdditionalSettings.tsx deleted file mode 100644 index 4ce04117dd9..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/AdditionalSettings.tsx +++ /dev/null @@ -1,87 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/AdditionalSettings.tsx -import { css } from '@emotion/css'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { Trans, t } from '@grafana/i18n'; -import { Icon, Switch, Tooltip, useTheme2 } from '@grafana/ui'; - -import { metricsModaltestIds } from './shared/testIds'; -import { AdditionalSettingsProps } from './shared/types'; -import { getPlaceholders } from './state/helpers'; - -export function AdditionalSettings(props: AdditionalSettingsProps) { - const { state, onChangeFullMetaSearch, onChangeIncludeNullMetadata, onChangeDisableTextWrap, onChangeUseBackend } = - props; - - const theme = useTheme2(); - const styles = getStyles(theme); - - const placeholders = getPlaceholders(); - - return ( - <> -
- onChangeFullMetaSearch()} - /> -
{placeholders.metadataSearchSwitch}
-
-
- onChangeIncludeNullMetadata()} - /> -
{placeholders.includeNullMetadata}
-
-
- onChangeDisableTextWrap()} /> -
- - Disable text wrap - -
-
-
- onChangeUseBackend()} - /> -
{placeholders.setUseBackend} 
- - - -
- - ); -} - -function getStyles(theme: GrafanaTheme2) { - return { - settingsIcon: css({ - color: theme.colors.text.secondary, - }), - selectItem: css({ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - padding: '4px 0', - }), - selectItemLabel: css({ - margin: `0 0 0 ${theme.spacing(1)}`, - alignSelf: 'center', - color: theme.colors.text.secondary, - fontSize: '12px', - }), - }; -} diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.test.tsx index c85089657cb..f57fbb59bd6 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.test.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.test.tsx @@ -12,7 +12,7 @@ import { PromOptions } from '../../../types'; import { PromVisualQuery } from '../../types'; import { MetricsModal } from './MetricsModal'; -import { metricsModaltestIds } from './shared/testIds'; +import { metricsModaltestIds } from './testIds'; // don't care about interaction tracking in our unit tests jest.mock('@grafana/runtime', () => ({ @@ -117,28 +117,6 @@ describe('MetricsModal', () => { }); }); - it('shows results metrics per page chosen by the user', async () => { - setup(defaultQuery, listOfMetrics); - const resultsPerPageInput = screen.getByTestId(metricsModaltestIds.resultsPerPage); - await userEvent.type(resultsPerPageInput, '12'); - const metricInsideRange = screen.getByText('j'); - expect(metricInsideRange).toBeInTheDocument(); - }); - - it('paginates lots of metrics and does not run out of memory', async () => { - const lotsOfMetrics: string[] = [...Array(100000).keys()].map((i) => '' + i); - setup(defaultQuery, lotsOfMetrics); - await waitFor(() => { - // doesn't break on loading - expect(screen.getByText('0')).toBeInTheDocument(); - }); - const resultsPerPageInput = screen.getByTestId(metricsModaltestIds.resultsPerPage); - // doesn't break on changing results per page - await userEvent.type(resultsPerPageInput, '11'); - const metricInsideRange = screen.getByText('9'); - expect(metricInsideRange).toBeInTheDocument(); - }); - // Fuzzy search it('searches and filter by metric name with a fuzzy search', async () => { // search for a_bucket by name @@ -171,14 +149,6 @@ describe('MetricsModal', () => { expect(metricABucket).toBeInTheDocument(); }); - const showSettingsButton = screen.getByTestId(metricsModaltestIds.showAdditionalSettings); - expect(showSettingsButton).toBeInTheDocument(); - await userEvent.click(showSettingsButton); - - const metadataSwitch = screen.getByTestId(metricsModaltestIds.searchWithMetadata); - expect(metadataSwitch).toBeInTheDocument(); - await userEvent.click(metadataSwitch); - const searchMetric = screen.getByTestId(metricsModaltestIds.searchMetric); expect(searchMetric).toBeInTheDocument(); await userEvent.type(searchMetric, 'functions'); @@ -248,6 +218,34 @@ function createDatasource(withLabels?: boolean) { const languageProvider = new EmptyLanguageProviderMock() as unknown as PrometheusLanguageProviderInterface; // display different results if their labels are selected in the PromVisualQuery + if (withLabels) { + languageProvider.queryMetricsMetadata = jest.fn().mockResolvedValue({ + 'with-labels': { + type: 'with-labels-type', + help: 'with-labels-help', + }, + }); + } else { + // all metrics - create metadata for all metrics in listOfMetrics + const mockMetadata: Record = {}; + listOfMetrics.forEach((metric) => { + if (metric === 'all-metrics') { + mockMetadata[metric] = { type: 'all-metrics-type', help: 'all-metrics-help' }; + } else if (metric === 'a_bucket') { + mockMetadata[metric] = { type: 'histogram', help: 'for functions' }; + } else if (metric === 'new_histogram') { + mockMetadata[metric] = { type: 'histogram', help: 'a native histogram' }; + } else if (metric === 'a') { + mockMetadata[metric] = { type: 'counter', help: 'a-metric-help' }; + } else { + mockMetadata[metric] = { type: 'counter', help: `${metric} metric help` }; + } + }); + + languageProvider.queryMetricsMetadata = jest.fn().mockResolvedValue(mockMetadata); + } + + // Also mock the retrieveMetricsMetadata method that might be used elsewhere if (withLabels) { languageProvider.retrieveMetricsMetadata = jest.fn().mockReturnValue({ 'with-labels': { @@ -256,26 +254,23 @@ function createDatasource(withLabels?: boolean) { }, }); } else { - // all metrics - languageProvider.retrieveMetricsMetadata = jest.fn().mockReturnValue({ - 'all-metrics': { - type: 'all-metrics-type', - help: 'all-metrics-help', - }, - a: { - type: 'counter', - help: 'a-metric-help', - }, - a_bucket: { - type: 'histogram', - help: 'for functions', - }, - new_histogram: { - type: 'histogram', - help: 'a native histogram', - }, - // missing metadata for other metrics is tested for, see below + // Create the same metadata structure for retrieveMetricsMetadata + const mockMetadata: Record = {}; + listOfMetrics.forEach((metric) => { + if (metric === 'all-metrics') { + mockMetadata[metric] = { type: 'all-metrics-type', help: 'all-metrics-help' }; + } else if (metric === 'a_bucket') { + mockMetadata[metric] = { type: 'histogram', help: 'for functions' }; + } else if (metric === 'new_histogram') { + mockMetadata[metric] = { type: 'histogram', help: 'a native histogram' }; + } else if (metric === 'a') { + mockMetadata[metric] = { type: 'counter', help: 'a-metric-help' }; + } else { + mockMetadata[metric] = { type: 'counter', help: `${metric} metric help` }; + } }); + + languageProvider.retrieveMetricsMetadata = jest.fn().mockReturnValue(mockMetadata); } const datasource = new PrometheusDatasource( diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx index 0722489b771..59c4c703ccf 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModal.tsx @@ -1,107 +1,49 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/MetricsModal.tsx import { cx } from '@emotion/css'; -import debounce from 'debounce-promise'; -import { useCallback, useEffect, useMemo, useReducer } from 'react'; -import { SelectableValue } from '@grafana/data'; +import { SelectableValue, TimeRange } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { t, Trans } from '@grafana/i18n'; -import { - Button, - ButtonGroup, - Icon, - Input, - Modal, - MultiSelect, - Pagination, - Spinner, - Toggletip, - useTheme2, -} from '@grafana/ui'; +import { Icon, Input, Modal, MultiSelect, Pagination, Spinner, useStyles2 } from '@grafana/ui'; -import { getDebounceTimeInMilliseconds } from '../../../caching'; -import { METRIC_LABEL } from '../../../constants'; -import { regexifyLabelValuesQueryString } from '../../parsingUtils'; -import { formatPrometheusLabelFilters } from '../shared/formatter'; +import { PrometheusDatasource } from '../../../datasource'; +import { PromVisualQuery } from '../../types'; -import { AdditionalSettings } from './AdditionalSettings'; import { FeedbackLink } from './FeedbackLink'; +import { MetricsModalContextProvider, useMetricsModal } from './MetricsModalContext'; import { ResultsTable } from './ResultsTable'; -import { metricsModaltestIds } from './shared/testIds'; -import { MetricsModalProps } from './shared/types'; -import { - calculatePageList, - calculateResultsPerPage, - displayedMetrics, - getPlaceholders, - getPromTypes, - setMetrics, - tracking, -} from './state/helpers'; -import { - buildMetrics, - DEFAULT_RESULTS_PER_PAGE, - filterMetricsBackend, - initialState, - MAXIMUM_RESULTS_PER_PAGE, - MetricsModalMetadata, - setDisableTextWrap, - setFullMetaSearch, - setFuzzySearchQuery, - setIncludeNullMetadata, - setIsLoading, - setMetaHaystack, - setNameHaystack, - setPageNum, - setResultsPerPage, - setSelectedTypes, - setUseBackend, - showAdditionalSettings, - stateSlice, -} from './state/state'; -import { getStyles } from './styles'; +import { getPlaceholders, getPromTypes } from './helpers'; +import { getMetricsModalStyles } from './styles'; +import { metricsModaltestIds } from './testIds'; import { PromFilterOption } from './types'; -import { debouncedFuzzySearch } from './uFuzzy'; -export const MetricsModal = (props: MetricsModalProps) => { - const { datasource, isOpen, onClose, onChange, query, initialMetrics, timeRange } = props; +interface MetricsModalProps { + datasource: PrometheusDatasource; + timeRange: TimeRange; + isOpen: boolean; + query: PromVisualQuery; + onClose: () => void; + onChange: (query: PromVisualQuery) => void; +} - const [state, dispatch] = useReducer(stateSlice.reducer, initialState(query)); +const MetricsModalContent = (props: MetricsModalProps) => { + const { isOpen, onClose, onChange, query, timeRange } = props; - const theme = useTheme2(); - const styles = getStyles(theme, state.disableTextWrap); + const { + isLoading, + filteredMetricsData, + debouncedBackendSearch, + pagination, + setPagination, + selectedTypes, + setSelectedTypes, + searchedText, + setSearchedText, + } = useMetricsModal(); + const styles = useStyles2(getMetricsModalStyles); const placeholders = getPlaceholders(); const promTypes = getPromTypes(); - /** - * loads metrics and metadata on opening modal and switching off useBackend - */ - const updateMetricsMetadata = useCallback(async () => { - // *** Loading Gif - dispatch(setIsLoading(true)); - - // Because Combobox in MetricsCombobox doesn't use the same lifecycle as Select to open the Metrics Explorer - // it might not have loaded any metrics yet, so it instead passes in an async function to get the metrics - const metrics = typeof initialMetrics === 'function' ? await initialMetrics() : initialMetrics; - - const data: MetricsModalMetadata = await setMetrics(datasource, query, metrics); - dispatch( - buildMetrics({ - isLoading: false, - hasMetadata: data.hasMetadata, - metrics: data.metrics, - metaHaystackDictionary: data.metaHaystackDictionary, - nameHaystackDictionary: data.nameHaystackDictionary, - totalMetricCount: data.metrics.length, - filteredMetricCount: data.metrics.length, - }) - ); - }, [query, datasource, initialMetrics]); - - useEffect(() => { - updateMetricsMetadata(); - }, [updateMetricsMetadata]); - const typeOptions: SelectableValue[] = promTypes.map((t: PromFilterOption) => { return { value: t.value, @@ -110,96 +52,10 @@ export const MetricsModal = (props: MetricsModalProps) => { }; }); - /** - * The backend debounced search - */ - const debouncedBackendSearch = useMemo( - () => - debounce(async (metricText: string) => { - dispatch(setIsLoading(true)); - - const queryString = regexifyLabelValuesQueryString(metricText); - const filterArray = query.labels ? formatPrometheusLabelFilters(query.labels) : []; - const match = `{__name__=~".*${queryString}"${filterArray ? filterArray.join('') : ''}}`; - - const results = await datasource.languageProvider.queryLabelValues(timeRange, METRIC_LABEL, match); - - const resultsOptions = results.map((result) => ({ - value: result, - })); - - dispatch( - filterMetricsBackend({ - metrics: resultsOptions, - filteredMetricCount: resultsOptions.length, - isLoading: false, - }) - ); - }, getDebounceTimeInMilliseconds(datasource.cacheLevel)), - [datasource.cacheLevel, datasource.languageProvider, query.labels, timeRange] - ); - - function fuzzyNameDispatch(haystackData: string[][]) { - dispatch(setNameHaystack(haystackData)); - } - - function fuzzyMetaDispatch(haystackData: string[][]) { - dispatch(setMetaHaystack(haystackData)); - } - - function searchCallback(query: string, fullMetaSearchVal: boolean) { - if (state.useBackend && query === '') { - // get all metrics data if a user erases everything in the input - updateMetricsMetadata(); - } else if (state.useBackend) { - debouncedBackendSearch(query); - } else { - // search either the names or all metadata - // fuzzy search go! - if (fullMetaSearchVal) { - debouncedFuzzySearch(Object.keys(state.metaHaystackDictionary), query, fuzzyMetaDispatch); - } else { - debouncedFuzzySearch(Object.keys(state.nameHaystackDictionary), query, fuzzyNameDispatch); - } - } - } - - /* Settings switches */ - const additionalSettings = ( - { - const newVal = !state.fullMetaSearch; - dispatch(setFullMetaSearch(newVal)); - onChange({ ...query, fullMetaSearch: newVal }); - searchCallback(state.fuzzySearchQuery, newVal); - }} - onChangeIncludeNullMetadata={() => { - dispatch(setIncludeNullMetadata(!state.includeNullMetadata)); - onChange({ ...query, includeNullMetadata: !state.includeNullMetadata }); - }} - onChangeDisableTextWrap={() => { - dispatch(setDisableTextWrap()); - onChange({ ...query, disableTextWrap: !state.disableTextWrap }); - tracking('grafana_prom_metric_encycopedia_disable_text_wrap_interaction', state, ''); - }} - onChangeUseBackend={() => { - const newVal = !state.useBackend; - dispatch(setUseBackend(newVal)); - onChange({ ...query, useBackend: newVal }); - if (newVal === false) { - // rebuild the metrics metadata if we turn off useBackend - updateMetricsMetadata(); - } else { - // check if there is text in the browse search and update - if (state.fuzzySearchQuery !== '') { - debouncedBackendSearch(state.fuzzySearchQuery); - } - // otherwise wait for user typing - } - }} - /> - ); + const searchCallback = (query: string, fullMetaSearchVal?: boolean) => { + setSearchedText(query); + debouncedBackendSearch(timeRange, query); + }; return ( { autoFocus={true} data-testid={metricsModaltestIds.searchMetric} placeholder={placeholders.browse} - value={state.fuzzySearchQuery} + value={searchedText} onInput={(e) => { const value = e.currentTarget.value ?? ''; - dispatch(setFuzzySearchQuery(value)); - searchCallback(value, state.fullMetaSearch); + setSearchedText(value); + setPagination({ ...pagination, pageNum: 1 }); + searchCallback(value); }} /> - {state.hasMetadata && ( -
- dispatch(setSelectedTypes(v))} - /> -
- )} -
- -
- - - -
+
+
@@ -298,61 +123,23 @@ export const MetricsModal = (props: MetricsModalProps) => { )}
- {state.metrics && ( - - )} + {filteredMetricsData && }
-
- - Showing {'{{num}}'} of {'{{count}}'} results - -
{ - const page = val ?? 1; - dispatch(setPageNum(page)); - }} + currentPage={pagination.pageNum > pagination.totalPageNum ? 1 : pagination.pageNum} + numberOfPages={pagination.totalPageNum} + onNavigate={(val: number) => setPagination({ ...pagination, pageNum: val ?? 1 })} /> -
-

- Results per page -

- { - const value = +e.currentTarget.value; - - if (isNaN(value) || value >= MAXIMUM_RESULTS_PER_PAGE) { - return; - } - - dispatch(setResultsPerPage(value)); - }} - /> -
); }; + +export const MetricsModal = (props: MetricsModalProps) => { + return ( + + + + ); +}; diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModalContext.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModalContext.test.tsx new file mode 100644 index 00000000000..955b2c1b585 --- /dev/null +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModalContext.test.tsx @@ -0,0 +1,329 @@ +import { act, render, renderHook, waitFor } from '@testing-library/react'; +import { ReactNode } from 'react'; + +import { TimeRange } from '@grafana/data'; + +import { PrometheusLanguageProviderInterface } from '../../../language_provider'; + +import { DEFAULT_RESULTS_PER_PAGE, MetricsModalContextProvider, useMetricsModal } from './MetricsModalContext'; +import { generateMetricData } from './helpers'; + +// Mock dependencies +jest.mock('./helpers', () => ({ + generateMetricData: jest.fn(), +})); + +const mockGenerateMetricData = generateMetricData as jest.MockedFunction; + +// Mock language provider +const mockLanguageProvider: PrometheusLanguageProviderInterface = { + queryMetricsMetadata: jest.fn(), + queryLabelValues: jest.fn(), + retrieveMetricsMetadata: jest.fn(), +} as unknown as PrometheusLanguageProviderInterface; + +// Helper to create wrapper component +const createWrapper = (languageProvider = mockLanguageProvider) => { + return ({ children }: { children: ReactNode }) => ( + {children} + ); +}; + +// Sample time range for tests +const defaultTimeRange: TimeRange = { + from: 'now-1h' as unknown as TimeRange['from'], + to: 'now' as unknown as TimeRange['to'], + raw: { + from: 'now-1h', + to: 'now', + }, +}; + +describe('MetricsModalContext', () => { + let consoleSpy: jest.SpyInstance; + + beforeEach(() => { + jest.clearAllMocks(); + // Mock console.error to suppress React act() warnings + consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + // Default mock implementations + mockGenerateMetricData.mockImplementation((metric) => ({ + value: metric, + type: 'counter', + description: 'Test metric', + })); + (mockLanguageProvider.queryMetricsMetadata as jest.Mock).mockResolvedValue({ + test_metric: { type: 'counter', help: 'Test metric' }, + }); + }); + + afterEach(() => { + consoleSpy.mockRestore(); + }); + + describe('useMetricsModal hook', () => { + it('should throw error when used outside provider', () => { + expect(() => { + renderHook(() => useMetricsModal()); + }).toThrow('useMetricsModal must be used within a MetricsModalContextProvider'); + }); + + it('should provide context value when used within provider', () => { + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + expect(result.current).toBeDefined(); + expect(result.current.isLoading).toBe(true); // Initially loading + expect(result.current.filteredMetricsData).toEqual([]); + expect(result.current.pagination).toEqual({ + pageNum: 1, + totalPageNum: 1, + resultsPerPage: DEFAULT_RESULTS_PER_PAGE, + }); + expect(result.current.selectedTypes).toEqual([]); + expect(result.current.searchedText).toBe(''); + }); + }); + + describe('State management', () => { + it('should update pagination', () => { + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + const expectedPagination = { pageNum: 1, resultsPerPage: 50, totalPageNum: 1 }; + + act(() => { + result.current.setPagination({ pageNum: 2, resultsPerPage: 50, totalPageNum: 3 }); + }); + + expect(result.current.pagination).toEqual(expectedPagination); + }); + + it('should update selected types', () => { + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + const newTypes = [{ value: 'counter', label: 'Counter' }]; + + act(() => { + result.current.setSelectedTypes(newTypes); + }); + + expect(result.current.selectedTypes).toEqual(newTypes); + }); + + it('should update searched text', () => { + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + act(() => { + result.current.setSearchedText('test_metric'); + }); + + expect(result.current.searchedText).toBe('test_metric'); + }); + + it('should update loading state', () => { + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + act(() => { + result.current.setIsLoading(false); + }); + + expect(result.current.isLoading).toBe(false); + }); + }); + + describe('Metadata fetching', () => { + it('should load initial metadata on mount', async () => { + const mockMetadata = { + cpu_usage: { type: 'gauge', help: 'CPU usage percentage' }, + memory_usage: { type: 'gauge', help: 'Memory usage bytes' }, + }; + + (mockLanguageProvider.queryMetricsMetadata as jest.Mock).mockResolvedValue(mockMetadata); + + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + // Wait for metadata to load + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + expect(mockLanguageProvider.queryMetricsMetadata).toHaveBeenCalledWith(1000); + expect(mockGenerateMetricData).toHaveBeenCalledWith('cpu_usage', mockLanguageProvider); + expect(mockGenerateMetricData).toHaveBeenCalledWith('memory_usage', mockLanguageProvider); + expect(result.current.filteredMetricsData).toHaveLength(2); + }); + + it('should handle empty metadata response', async () => { + (mockLanguageProvider.queryMetricsMetadata as jest.Mock).mockResolvedValue({}); + + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + expect(result.current.filteredMetricsData).toEqual([]); + }); + + it('should handle metadata fetch error', async () => { + (mockLanguageProvider.queryMetricsMetadata as jest.Mock).mockRejectedValue(new Error('Network error')); + + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + expect(result.current.filteredMetricsData).toEqual([]); + }); + }); + + describe('Backend search', () => { + it('should perform backend search with results', async () => { + (mockLanguageProvider.queryLabelValues as jest.Mock).mockResolvedValue(['test_metric', 'other_metric']); + + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + await act(async () => { + await result.current.debouncedBackendSearch(defaultTimeRange, 'test'); + }); + + expect(mockLanguageProvider.queryLabelValues).toHaveBeenCalledWith( + defaultTimeRange, + '__name__', + '{__name__=~"(?i).*test.*"}' + ); + expect(result.current.filteredMetricsData).toHaveLength(1); + }); + + it('should handle backend search error', async () => { + (mockLanguageProvider.queryLabelValues as jest.Mock).mockRejectedValue(new Error('Search failed')); + + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + await act(async () => { + await result.current.debouncedBackendSearch(defaultTimeRange, 'test'); + }); + + expect(result.current.filteredMetricsData).toEqual([]); + expect(result.current.isLoading).toBe(false); + }); + }); + + describe('Filtering logic', () => { + it('should return all metrics when no types are selected', async () => { + mockGenerateMetricData.mockImplementation((metric) => ({ + value: metric, + type: 'counter', + description: 'Test metric', + })); + + (mockLanguageProvider.queryMetricsMetadata as jest.Mock).mockResolvedValue({ + test_metric: { type: 'counter', help: 'Test metric' }, + }); + + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + expect(result.current.filteredMetricsData).toHaveLength(1); + expect(result.current.selectedTypes).toEqual([]); + }); + + it('should filter metrics by selected type', async () => { + mockGenerateMetricData.mockImplementation((metric) => ({ + value: metric, + type: metric === 'counter_metric' ? 'counter' : 'gauge', + description: 'Test metric', + })); + + (mockLanguageProvider.queryMetricsMetadata as jest.Mock).mockResolvedValue({ + counter_metric: { type: 'counter', help: 'Counter metric' }, + gauge_metric: { type: 'gauge', help: 'Gauge metric' }, + }); + + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + act(() => { + result.current.setSelectedTypes([{ value: 'counter', label: 'Counter' }]); + }); + + expect(result.current.filteredMetricsData).toHaveLength(1); + expect(result.current.filteredMetricsData[0].value).toBe('counter_metric'); + }); + + it('should handle metrics without type when "no type" is selected', async () => { + mockGenerateMetricData.mockImplementation((metric) => ({ + value: metric, + type: metric === 'no_type_metric' ? undefined : 'counter', + description: 'Test metric', + })); + + (mockLanguageProvider.queryMetricsMetadata as jest.Mock).mockResolvedValue({ + counter_metric: { type: 'counter', help: 'Counter metric' }, + no_type_metric: { help: 'Metric without type' }, + }); + + const { result } = renderHook(() => useMetricsModal(), { + wrapper: createWrapper(), + }); + + await waitFor(() => { + expect(result.current.isLoading).toBe(false); + }); + + act(() => { + result.current.setSelectedTypes([{ value: 'no type', label: 'No Type' }]); + }); + + expect(result.current.filteredMetricsData).toHaveLength(1); + expect(result.current.filteredMetricsData[0].value).toBe('no_type_metric'); + }); + }); + + describe('Component integration', () => { + it('should render provider without errors', () => { + const TestComponent = () => { + return
frontend
; + }; + + const { getByTestId } = render( + + + + ); + + expect(getByTestId('test')).toHaveTextContent('frontend'); + }); + }); +}); diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModalContext.tsx b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModalContext.tsx new file mode 100644 index 00000000000..3361b448547 --- /dev/null +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/MetricsModalContext.tsx @@ -0,0 +1,201 @@ +import debounce from 'debounce-promise'; +import { + createContext, + FC, + PropsWithChildren, + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; + +import { SelectableValue, TimeRange } from '@grafana/data'; + +import { METRIC_LABEL, PROMETHEUS_QUERY_BUILDER_MAX_RESULTS } from '../../../constants'; +import { PrometheusLanguageProviderInterface } from '../../../language_provider'; +import { regexifyLabelValuesQueryString } from '../../parsingUtils'; +import { QueryBuilderLabelFilter } from '../../shared/types'; +import { formatPrometheusLabelFilters } from '../formatter'; + +import { generateMetricData } from './helpers'; +import { MetricData, MetricsData } from './types'; +import { fuzzySearch } from './uFuzzy'; + +export const DEFAULT_RESULTS_PER_PAGE = 25; + +type Pagination = { + pageNum: number; + resultsPerPage: number; + totalPageNum: number; +}; + +type MetricsModalContextValue = { + isLoading: boolean; + setIsLoading: (val: boolean) => void; + filteredMetricsData: MetricData[]; + debouncedBackendSearch: ( + timeRange: TimeRange, + metricText: string, + queryLabels?: QueryBuilderLabelFilter[] + ) => Promise; + pagination: Pagination; + setPagination: (val: Pagination) => void; + selectedTypes: Array>; + setSelectedTypes: (val: Array>) => void; + searchedText: string; + setSearchedText: (val: string) => void; +}; + +const MetricsModalContext = createContext(undefined); + +type MetricsModalContextProviderProps = { + languageProvider: PrometheusLanguageProviderInterface; +}; + +export const MetricsModalContextProvider: FC> = ({ + children, + languageProvider, +}) => { + const [isLoading, setIsLoading] = useState(true); + const [metricsData, setMetricsData] = useState([]); + const [pagination, setPagination] = useState({ + pageNum: 1, + totalPageNum: 1, + resultsPerPage: DEFAULT_RESULTS_PER_PAGE, + }); + const [selectedTypes, setSelectedTypes] = useState>>([]); + const [searchedText, setSearchedText] = useState(''); + + const filteredMetricsData = useMemo(() => { + if (selectedTypes.length === 0) { + return metricsData; + } + + // Filter metrics based on selected types + return metricsData.filter((metric: MetricData) => { + return selectedTypes.some((selectedType) => { + // Handle metrics with defined types + if (metric.type && selectedType.value) { + return metric.type.includes(selectedType.value); + } + + // Handle metrics without type when "no type" is selected + if (!metric.type && selectedType.value === 'no type') { + return true; + } + + return false; + }); + }); + }, [metricsData, selectedTypes]); + + useEffect(() => { + const totalPageNum = + filteredMetricsData.length === 0 ? 1 : Math.ceil(filteredMetricsData.length / pagination.resultsPerPage); + const pageNum = pagination.pageNum > totalPageNum ? 1 : pagination.pageNum; + + setPagination((prevPagination) => ({ + ...prevPagination, + totalPageNum, + pageNum, + })); + }, [filteredMetricsData.length, pagination.resultsPerPage, pagination.pageNum]); + + // Track the latest search ID to handle race conditions + const latestSearchIdRef = useRef(0); + + const fetchMetadata = useCallback(async () => { + try { + setIsLoading(true); + const metadata = await languageProvider.queryMetricsMetadata(PROMETHEUS_QUERY_BUILDER_MAX_RESULTS); + + if (Object.keys(metadata).length === 0) { + setMetricsData([]); + } else { + const processedData = Object.keys(metadata).map((m) => generateMetricData(m, languageProvider)); + setMetricsData(processedData); + } + } catch (error) { + setMetricsData([]); + } finally { + setIsLoading(false); + } + }, [languageProvider]); + + const debouncedBackendSearch = useMemo( + () => + debounce(async (timeRange: TimeRange, metricText: string, queryLabels?: QueryBuilderLabelFilter[]) => { + // Generate unique search ID to handle race conditions + const searchId = ++latestSearchIdRef.current; + + try { + if (metricText === '') { + await fetchMetadata(); + return; + } + + setIsLoading(true); + + const queryString = regexifyLabelValuesQueryString(metricText); + const filterArray = queryLabels ? formatPrometheusLabelFilters(queryLabels) : []; + const match = `{__name__=~"(?i).*${queryString}"${filterArray ? filterArray.join('') : ''}}`; + + const results = await languageProvider.queryLabelValues(timeRange, METRIC_LABEL, match); + + // Check if this is still the most recent search + if (searchId !== latestSearchIdRef.current) { + return; // Ignore outdated results + } + + const [fuzzyOrderedMetrics] = fuzzySearch(results, queryString); + const resultsOptions: MetricsData = fuzzyOrderedMetrics.map((m) => generateMetricData(m, languageProvider)); + + setMetricsData(resultsOptions); + setIsLoading(false); + } catch (error) { + // Only update state if this is still the latest search + if (searchId === latestSearchIdRef.current) { + console.error('Backend search failed:', error); + setMetricsData([]); // Clear results on error + setIsLoading(false); + } + } + }, 300), + [fetchMetadata, languageProvider] + ); + + useEffect(() => { + fetchMetadata(); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + + {children} + + ); +}; + +export function useMetricsModal() { + const context = useContext(MetricsModalContext); + if (context === undefined) { + throw new Error('useMetricsModal must be used within a MetricsModalContextProvider'); + } + return context; +} diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/ResultsTable.tsx b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/ResultsTable.tsx index 1e179d336ee..3f3a1bf5afc 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/ResultsTable.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/ResultsTable.tsx @@ -1,65 +1,62 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/ResultsTable.tsx -import { css } from '@emotion/css'; -import { ReactElement } from 'react'; +import { ReactElement, useMemo } from 'react'; import Highlighter from 'react-highlight-words'; -import { GrafanaTheme2 } from '@grafana/data'; import { t, Trans } from '@grafana/i18n'; -import { Button, Icon, Tooltip, useTheme2 } from '@grafana/ui'; +import { Icon, Tooltip, useStyles2 } from '@grafana/ui'; import { docsTip } from '../../../configuration/shared/utils'; import { PromVisualQuery } from '../../types'; -import { tracking } from './state/helpers'; -import { MetricsModalState } from './state/state'; -import { MetricData, MetricsData } from './types'; +import { useMetricsModal } from './MetricsModalContext'; +import { getResultsTableStyles } from './styles'; +import { MetricData } from './types'; type ResultsTableProps = { - metrics: MetricsData; onChange: (query: PromVisualQuery) => void; onClose: () => void; query: PromVisualQuery; - state: MetricsModalState; - disableTextWrap: boolean; }; export function ResultsTable(props: ResultsTableProps) { - const { metrics, onChange, onClose, query, state, disableTextWrap } = props; + const { onChange, onClose, query } = props; + const { + isLoading, + filteredMetricsData, + pagination: { pageNum, resultsPerPage }, + selectedTypes, + searchedText, + } = useMetricsModal(); - const theme = useTheme2(); - const styles = getStyles(theme, disableTextWrap); + const slicedMetrics = useMemo(() => { + const startIndex = (pageNum - 1) * resultsPerPage; + const endIndex = startIndex + resultsPerPage; + return filteredMetricsData.slice(startIndex, endIndex); + }, [filteredMetricsData, pageNum, resultsPerPage]); + + const styles = useStyles2(getResultsTableStyles); function selectMetric(metric: MetricData) { if (metric.value) { onChange({ ...query, metric: metric.value }); - tracking('grafana_prom_metric_encycopedia_tracking', state, metric.value); onClose(); } } function metaRows(metric: MetricData) { - if (state.fullMetaSearch && metric) { - return ( - <> - {displayType(metric.type ?? '')} - - - - - ); - } else { - return ( - <> - {displayType(metric.type ?? '')} - {metric.description ?? ''} - - ); - } + return ( + <> + {displayType(metric.type ?? '')} + + + + + ); } function addHelpIcon(fullType: string, descriptiveType: string, link: string) { @@ -105,7 +102,7 @@ export function ResultsTable(props: ResultsTableProps) { function noMetricsMessages(): ReactElement { let message; - if (!state.fuzzySearchQuery) { + if (!searchedText) { message = t( 'grafana-prometheus.querybuilder.results-table.message-no-metrics-found', 'There are no metrics found in the data source.' @@ -119,7 +116,7 @@ export function ResultsTable(props: ResultsTableProps) { ); } - if (state.fuzzySearchQuery || state.selectedTypes.length > 0) { + if (searchedText || selectedTypes.length > 0) { message = t( 'grafana-prometheus.querybuilder.results-table.message-expand-search', 'There are no metrics found. Try to expand your search and filters.' @@ -133,21 +130,6 @@ export function ResultsTable(props: ResultsTableProps) { ); } - function textHighlight(state: MetricsModalState) { - if (state.useBackend) { - // highlight the input only for the backend search - // this highlight is equivalent to how the metric select highlights - // look into matching on regex input - return [state.fuzzySearchQuery]; - } else if (state.fullMetaSearch) { - // highlight the matches in the ufuzzy metaHaystack - return state.metaHaystackMatches; - } else { - // highlight the ufuzzy name matches - return state.nameHaystackMatches; - } - } - return ( @@ -155,115 +137,35 @@ export function ResultsTable(props: ResultsTableProps) { - {state.hasMetadata && ( - <> - - - - )} - + + <> - {metrics.length > 0 && - metrics.map((metric: MetricData, idx: number) => { + {slicedMetrics.length > 0 && + slicedMetrics.map((metric: MetricData, idx: number) => { return ( - + selectMetric(metric)}> - {state.hasMetadata && metaRows(metric)} - + {metaRows(metric)} ); })} - {metrics.length === 0 && !state.isLoading && noMetricsMessages()} + {slicedMetrics.length === 0 && !isLoading && noMetricsMessages()}
Name - Type - - Description - + Type + + Description +
- -
); } - -const getStyles = (theme: GrafanaTheme2, disableTextWrap: boolean) => { - return { - table: css({ - tableLayout: disableTextWrap ? undefined : 'fixed', - borderRadius: theme.shape.radius.default, - width: '100%', - whiteSpace: disableTextWrap ? 'nowrap' : 'normal', - td: { - padding: theme.spacing(1), - }, - 'td,th': { - minWidth: theme.spacing(3), - borderBottom: `1px solid ${theme.colors.border.weak}`, - }, - }), - row: css({ - label: 'row', - borderBottom: `1px solid ${theme.colors.border.weak}`, - '&:last-child': { - borderBottom: 0, - }, - }), - tableHeaderPadding: css({ - padding: '8px', - }), - matchHighLight: css({ - background: 'inherit', - color: theme.components.textHighlight.text, - backgroundColor: theme.components.textHighlight.background, - }), - nameWidth: css({ - width: disableTextWrap ? undefined : '37.5%', - }), - nameOverflow: css({ - overflowWrap: disableTextWrap ? undefined : 'anywhere', - }), - typeWidth: css({ - width: disableTextWrap ? undefined : '15%', - }), - descriptionWidth: css({ - width: disableTextWrap ? undefined : '35%', - }), - selectButtonWidth: css({ - width: disableTextWrap ? undefined : '12.5%', - }), - stickyHeader: css({ - position: 'sticky', - top: 0, - backgroundColor: theme.colors.background.primary, - }), - noResults: css({ - textAlign: 'center', - color: theme.colors.text.secondary, - }), - tooltipSpace: css({ - marginLeft: '4px', - }), - centerButton: css({ - display: 'block', - margin: 'auto', - border: 'none', - }), - }; -}; diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/helpers.test.ts b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/helpers.test.ts new file mode 100644 index 00000000000..d3998e46de0 --- /dev/null +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/helpers.test.ts @@ -0,0 +1,313 @@ +import { PrometheusLanguageProviderInterface } from '../../../language_provider'; +import { PromMetricsMetadata } from '../../../types'; + +import { calculatePageList, generateMetricData, getPlaceholders, getPromTypes } from './helpers'; +import { MetricsData } from './types'; + +// Mock the language provider +const createMockLanguageProvider = (metadata: PromMetricsMetadata = {}): PrometheusLanguageProviderInterface => + ({ + retrieveMetricsMetadata: jest.fn().mockReturnValue(metadata), + }) as unknown as PrometheusLanguageProviderInterface; + +describe('helpers.ts', () => { + describe('generateMetricData', () => { + it('should generate basic metric data', () => { + const mockProvider = createMockLanguageProvider({ + test_metric: { + type: 'counter', + help: 'Test counter metric', + }, + }); + + const result = generateMetricData('test_metric', mockProvider); + + expect(result).toEqual({ + value: 'test_metric', + type: 'counter', + description: 'Test counter metric', + }); + }); + + it('should handle metric with no metadata', () => { + const mockProvider = createMockLanguageProvider({}); + + const result = generateMetricData('unknown_metric', mockProvider); + + expect(result).toEqual({ + value: 'unknown_metric', + type: undefined, + description: undefined, + }); + }); + + it('should enhance type based on description for histogram', () => { + const mockProvider = createMockLanguageProvider({ + test_metric: { + type: 'gauge', + help: 'This is a histogram metric for testing', + }, + }); + + const result = generateMetricData('test_metric', mockProvider); + + expect(result.type).toBe('gauge (histogram)'); + }); + + it('should enhance type based on description for summary', () => { + const mockProvider = createMockLanguageProvider({ + test_metric: { + type: 'counter', + help: 'This is a summary metric for testing', + }, + }); + + const result = generateMetricData('test_metric', mockProvider); + + expect(result.type).toBe('counter (summary)'); + }); + + it('should not enhance type if already matches description', () => { + const mockProvider = createMockLanguageProvider({ + test_metric: { + type: 'histogram', + help: 'This is a histogram metric for testing', + }, + }); + + const result = generateMetricData('test_metric', mockProvider); + + expect(result.type).toBe('native histogram'); // Should be native histogram, not enhanced + }); + + it('should detect native histogram vs classic histogram', () => { + const mockProvider = createMockLanguageProvider({ + native_histogram: { + type: 'histogram', + help: 'Native histogram', + }, + classic_bucket: { + type: 'histogram', + help: 'Classic histogram', + }, + classic_bucket_with_labels: { + type: 'histogram', + help: 'Classic histogram with labels', + }, + }); + + // Native histogram (no _bucket suffix) + const nativeResult = generateMetricData('native_histogram', mockProvider); + expect(nativeResult.type).toBe('native histogram'); + + // Classic histogram (with _bucket suffix) + const classicResult = generateMetricData('classic_bucket', mockProvider); + expect(classicResult.type).toBe('histogram'); + + // Classic histogram with labels + const classicWithLabelsResult = generateMetricData('classic_bucket_with_labels', mockProvider); + expect(classicWithLabelsResult.type).toBe('native histogram'); // No _bucket pattern match + }); + + it('should handle old histogram pattern matching', () => { + const mockProvider = createMockLanguageProvider({ + test_bucket: { type: 'histogram', help: 'Test bucket metric' }, + 'test_bucket{le="0.1"}': { type: 'histogram', help: 'Test bucket with labels' }, + test_histogram: { type: 'histogram', help: 'Test histogram metric' }, + }); + + // Should be classic histogram (matches pattern) + expect(generateMetricData('test_bucket', mockProvider).type).toBe('histogram'); + expect(generateMetricData('test_bucket{le="0.1"}', mockProvider).type).toBe('histogram'); + + // Should be native histogram (doesn't match pattern) + expect(generateMetricData('test_histogram', mockProvider).type).toBe('native histogram'); + }); + + it('should handle case-insensitive description matching', () => { + const mockProvider = createMockLanguageProvider({ + test_metric: { + type: 'gauge', + help: 'This is a HISTOGRAM metric for testing', + }, + }); + + const result = generateMetricData('test_metric', mockProvider); + expect(result.type).toBe('gauge (histogram)'); + }); + + it('should handle empty type with description enhancement', () => { + const mockProvider = createMockLanguageProvider({ + test_metric: { + type: '', + help: 'This is a histogram metric for testing', + }, + }); + + const result = generateMetricData('test_metric', mockProvider); + expect(result.type).toBe('native histogram'); + }); + + it('should handle empty description', () => { + const mockProvider = createMockLanguageProvider({ + test_metric: { + type: 'gauge', + help: '', + }, + }); + + const result = generateMetricData('test_metric', mockProvider); + expect(result.type).toBe('gauge'); + }); + }); + + describe('calculatePageList', () => { + const createMetricsData = (length: number): MetricsData => + Array.from({ length }, (_, i) => ({ value: `metric_${i}` })); + + it('should return empty array for empty metrics data', () => { + expect(calculatePageList([], 10)).toEqual([]); + }); + + it('should return [1] for zero or negative results per page', () => { + const metricsData = createMetricsData(5); + expect(calculatePageList(metricsData, 0)).toEqual([1]); + expect(calculatePageList(metricsData, -5)).toEqual([1]); + }); + + it('should calculate correct page list for exact division', () => { + const metricsData = createMetricsData(20); + const result = calculatePageList(metricsData, 10); + expect(result).toEqual([1, 2]); + }); + + it('should calculate correct page list for non-exact division', () => { + const metricsData = createMetricsData(23); + const result = calculatePageList(metricsData, 10); + expect(result).toEqual([1, 2, 3]); + }); + + it('should handle single page scenario', () => { + const metricsData = createMetricsData(5); + const result = calculatePageList(metricsData, 10); + expect(result).toEqual([1]); + }); + + it('should handle very large datasets', () => { + const metricsData = createMetricsData(1000); + const result = calculatePageList(metricsData, 25); + expect(result).toHaveLength(40); + expect(result[0]).toBe(1); + expect(result[result.length - 1]).toBe(40); + }); + + it('should handle single item per page', () => { + const metricsData = createMetricsData(3); + const result = calculatePageList(metricsData, 1); + expect(result).toEqual([1, 2, 3]); + }); + + it('should handle fractional results per page', () => { + const metricsData = createMetricsData(10); + expect(calculatePageList(metricsData, 3.5)).toEqual([1, 2, 3]); + }); + }); + + describe('getPromTypes', () => { + it('should return array of Prometheus types', () => { + const types = getPromTypes(); + + expect(Array.isArray(types)).toBe(true); + expect(types.length).toBe(7); + + const expectedValues = ['counter', 'gauge', 'histogram', 'native histogram', 'summary', 'unknown', 'no type']; + const actualValues = types.map((type) => type.value); + + expect(actualValues).toEqual(expectedValues); + }); + + it('should have correct structure for each type', () => { + const types = getPromTypes(); + + types.forEach((type) => { + expect(type).toHaveProperty('value'); + expect(type).toHaveProperty('label'); + expect(type).toHaveProperty('description'); + + expect(typeof type.value).toBe('string'); + expect(typeof type.label).toBe('string'); + expect(typeof type.description).toBe('string'); + + expect(type.value.length).toBeGreaterThan(0); + expect(type.label.length).toBeGreaterThan(0); + expect(type.description.length).toBeGreaterThan(0); + }); + }); + + it('should return consistent results on multiple calls', () => { + const types1 = getPromTypes(); + const types2 = getPromTypes(); + + expect(types1).toEqual(types2); + }); + + it('should have unique values', () => { + const types = getPromTypes(); + const values = types.map((type) => type.value); + const uniqueValues = [...new Set(values)]; + + expect(values.length).toBe(uniqueValues.length); + }); + + it('should have descriptive labels', () => { + const types = getPromTypes(); + + types.forEach((type) => { + expect(type.label).not.toBe(type.value); + expect(type.label.length).toBeGreaterThanOrEqual(type.value.length); + }); + }); + }); + + describe('getPlaceholders', () => { + it('should return object with all required placeholders', () => { + const placeholders = getPlaceholders(); + + const expectedKeys = ['browse', 'filterType']; + + expect(Object.keys(placeholders)).toEqual(expectedKeys); + }); + + it('should have string values for all placeholders', () => { + const placeholders = getPlaceholders(); + + Object.values(placeholders).forEach((placeholder) => { + expect(typeof placeholder).toBe('string'); + expect(placeholder.length).toBeGreaterThan(0); + }); + }); + + it('should return consistent results on multiple calls', () => { + const placeholders1 = getPlaceholders(); + const placeholders2 = getPlaceholders(); + + expect(placeholders1).toEqual(placeholders2); + }); + + it('should contain expected placeholder content', () => { + const placeholders = getPlaceholders(); + + expect(placeholders.browse).toMatch(/search/i); + expect(placeholders.filterType).toMatch(/type/i); + }); + + it('should have descriptive placeholder text', () => { + const placeholders = getPlaceholders(); + + Object.values(placeholders).forEach((placeholder) => { + expect(placeholder.length).toBeGreaterThan(5); + expect(placeholder).toMatch(/[a-zA-Z]/); + }); + }); + }); +}); diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/helpers.ts b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/helpers.ts new file mode 100644 index 00000000000..9377a22e5e0 --- /dev/null +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/helpers.ts @@ -0,0 +1,123 @@ +// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/helpers.ts +import { t } from '@grafana/i18n'; + +import { PrometheusLanguageProviderInterface } from '../../../language_provider'; + +import { MetricData, MetricsData, PromFilterOption } from './types'; + +// Constants +const HISTOGRAM_TYPES = ['histogram', 'summary'] as const; +const OLD_HISTOGRAM_PATTERN = /^\w+_bucket$|^\w+_bucket{.*}$/; +const HISTOGRAM_TYPE = 'histogram'; +const NATIVE_HISTOGRAM_TYPE = 'native histogram'; + +/** + * Builds the metric data object with type and description + * @param metric - The metric name + * @param languageProvider - The Prometheus language provider interface + * @returns MetricData object with value, type, and description + */ +export const generateMetricData = ( + metric: string, + languageProvider: PrometheusLanguageProviderInterface +): MetricData => { + const metadata = languageProvider.retrieveMetricsMetadata(); + + let type = metadata[metric]?.type; + const description = metadata[metric]?.help; + + HISTOGRAM_TYPES.forEach((t) => { + if (description?.toLowerCase().includes(t) && type !== t) { + type = type ? `${type} (${t})` : t; + } + }); + + const oldHistogramMatch = metric.match(OLD_HISTOGRAM_PATTERN); + + if (type === HISTOGRAM_TYPE && !oldHistogramMatch) { + type = NATIVE_HISTOGRAM_TYPE; + } + + return { + value: metric, + type: type, + description: description, + }; +}; + +export function calculatePageList(metricsData: MetricsData, resultsPerPage: number): number[] { + if (!Array.isArray(metricsData) || metricsData.length === 0) { + return []; + } + + if (resultsPerPage <= 0) { + return [1]; + } + + const totalPages = Math.ceil(metricsData.length / resultsPerPage); + return Array.from({ length: totalPages }, (_, i) => i + 1); +} + +export const getPromTypes: () => PromFilterOption[] = () => [ + { + value: 'counter', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-counter', 'Counter'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-counter', + 'A cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart.' + ), + }, + { + value: 'gauge', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-gauge', 'Gauge'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-gauge', + 'A metric that represents a single numerical value that can arbitrarily go up and down.' + ), + }, + { + value: 'histogram', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-histogram', 'Histogram'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-histogram', + 'A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets.' + ), + }, + { + value: 'native histogram', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-native-histogram', 'Native histogram'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-native-histogram', + 'Native histograms are different from classic Prometheus histograms in a number of ways: Native histogram bucket boundaries are calculated by a formula that depends on the scale (resolution) of the native histogram, and are not user defined.' + ), + }, + { + value: 'summary', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-summary', 'Summary'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-summary', + 'A summary samples observations (usually things like request durations and response sizes) and can calculate configurable quantiles over a sliding time window.' + ), + }, + { + value: 'unknown', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-unknown', 'Unknown'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-unknown', + 'These metrics have been given the type unknown in the metadata.' + ), + }, + { + value: 'no type', + label: t('grafana-prometheus.querybuilder.get-prom-types.label-no-type', 'No type'), + description: t( + 'grafana-prometheus.querybuilder.get-prom-types.description-no-type', + 'These metrics have no defined type in the metadata.' + ), + }, +]; + +export const getPlaceholders = () => ({ + browse: t('grafana-prometheus.querybuilder.get-placeholders.browse', 'Search metrics by name'), + filterType: t('grafana-prometheus.querybuilder.get-placeholders.type', 'Filter by type'), +}); diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/shared/types.ts b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/shared/types.ts deleted file mode 100644 index 76ee015c1e9..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/shared/types.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { TimeRange } from '@grafana/data'; - -import { PrometheusDatasource } from '../../../../datasource'; -import { PromVisualQuery } from '../../../types'; - -interface MetricsModalState { - useBackend: boolean; - disableTextWrap: boolean; - includeNullMetadata: boolean; - fullMetaSearch: boolean; - hasMetadata: boolean; -} - -export interface MetricsModalProps { - datasource: PrometheusDatasource; - isOpen: boolean; - query: PromVisualQuery; - onClose: () => void; - onChange: (query: PromVisualQuery) => void; - initialMetrics: string[] | (() => Promise); - timeRange: TimeRange; -} - -export interface AdditionalSettingsProps { - state: MetricsModalState; - onChangeFullMetaSearch: () => void; - onChangeIncludeNullMetadata: () => void; - onChangeDisableTextWrap: () => void; - onChangeUseBackend: () => void; -} diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/state/helpers.ts b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/state/helpers.ts deleted file mode 100644 index 79d41287197..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/state/helpers.ts +++ /dev/null @@ -1,278 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/helpers.ts -import { AnyAction } from '@reduxjs/toolkit'; - -import { t } from '@grafana/i18n'; -import { reportInteraction } from '@grafana/runtime'; - -import { PrometheusDatasource } from '../../../../datasource'; -import { PromMetricsMetadata } from '../../../../types'; -import { PromVisualQuery } from '../../../types'; -import { HaystackDictionary, MetricData, MetricsData, PromFilterOption } from '../types'; - -import { MetricsModalMetadata, MetricsModalState, setFilteredMetricCount } from './state'; - -export async function setMetrics( - datasource: PrometheusDatasource, - query: PromVisualQuery, - initialMetrics?: string[] -): Promise { - // metadata is set in the metric select now - // use this to disable metadata search and display - let hasMetadata = true; - const metadata = datasource.languageProvider.retrieveMetricsMetadata(); - if (metadata && Object.keys(metadata).length === 0) { - hasMetadata = false; - } - - let nameHaystackDictionaryData: HaystackDictionary = {}; - let metaHaystackDictionaryData: HaystackDictionary = {}; - - // pass in metrics from getMetrics in the query builder, reduced in the metric select - let metricsData: MetricsData | undefined; - - metricsData = initialMetrics?.map((m: string) => { - const metricData = buildMetricData(m, datasource); - - const metaDataString = `${m}¦${metricData.description}`; - - nameHaystackDictionaryData[m] = metricData; - metaHaystackDictionaryData[metaDataString] = metricData; - - return metricData; - }); - - return { - isLoading: false, - hasMetadata: hasMetadata, - metrics: metricsData ?? [], - metaHaystackDictionary: metaHaystackDictionaryData, - nameHaystackDictionary: nameHaystackDictionaryData, - totalMetricCount: metricsData?.length ?? 0, - filteredMetricCount: metricsData?.length ?? 0, - }; -} - -/** - * Builds the metric data object with type and description - * - * @param metric The metric name - * @param datasource The Prometheus datasource for mapping metradata to the metric name - * @returns A MetricData object. - */ -function buildMetricData(metric: string, datasource: PrometheusDatasource): MetricData { - let type = getMetadataType(metric, datasource.languageProvider.retrieveMetricsMetadata()); - - const description = getMetadataHelp(metric, datasource.languageProvider.retrieveMetricsMetadata()); - - ['histogram', 'summary'].forEach((t) => { - if (description?.toLowerCase().includes(t) && type !== t) { - type += ` (${t})`; - } - }); - - const oldHistogramMatch = metric.match(/^\w+_bucket$|^\w+_bucket{.*}$/); - - if (type === 'histogram' && !oldHistogramMatch) { - type = 'native histogram'; - } - - const metricData: MetricData = { - value: metric, - type: type, - description: description, - }; - - return metricData; -} - -function getMetadataHelp(metric: string, metadata: PromMetricsMetadata): string | undefined { - return metadata[metric]?.help; -} - -function getMetadataType(metric: string, metadata: PromMetricsMetadata): string | undefined { - return metadata[metric]?.type; -} - -/** - * The filtered and paginated metrics displayed in the modal - * */ -export function displayedMetrics(state: MetricsModalState, dispatch: React.Dispatch) { - const filteredSorted: MetricsData = filterMetrics(state); - - if (!state.isLoading && state.filteredMetricCount !== filteredSorted.length) { - dispatch(setFilteredMetricCount(filteredSorted.length)); - } - - return sliceMetrics(filteredSorted, state.pageNum, state.resultsPerPage); -} - -/** - * Filter the metrics with all the options, fuzzy, type, null metadata - */ -function filterMetrics(state: MetricsModalState): MetricsData { - let filteredMetrics: MetricsData = state.metrics; - - if (state.fuzzySearchQuery && !state.useBackend) { - if (state.fullMetaSearch) { - filteredMetrics = state.metaHaystackOrder.map((needle: string) => state.metaHaystackDictionary[needle]); - } else { - filteredMetrics = state.nameHaystackOrder.map((needle: string) => state.nameHaystackDictionary[needle]); - } - } - - if (state.selectedTypes.length > 0) { - filteredMetrics = filteredMetrics.filter((m: MetricData, idx) => { - // Matches type - const matchesSelectedType = state.selectedTypes.some((t) => { - if (m.type && t.value) { - return m.type.includes(t.value); - } - - if (!m.type && t.value === 'no type') { - return true; - } - - return false; - }); - - // when a user filters for type, only return metrics with defined types - return matchesSelectedType; - }); - } - - if (!state.includeNullMetadata) { - filteredMetrics = filteredMetrics.filter((m: MetricData) => { - return m.type !== undefined && m.description !== undefined; - }); - } - - return filteredMetrics; -} - -export function calculatePageList(state: MetricsModalState) { - if (!state.metrics.length) { - return []; - } - - const calcResultsPerPage: number = state.resultsPerPage === 0 ? 1 : state.resultsPerPage; - - const pages = Math.floor(filterMetrics(state).length / calcResultsPerPage) + 1; - - return [...Array(pages).keys()].map((i) => i + 1); -} - -function sliceMetrics(metrics: MetricsData, pageNum: number, resultsPerPage: number) { - const calcResultsPerPage: number = resultsPerPage === 0 ? 1 : resultsPerPage; - const start: number = pageNum === 1 ? 0 : (pageNum - 1) * calcResultsPerPage; - const end: number = start + calcResultsPerPage; - return metrics.slice(start, end); -} - -export const calculateResultsPerPage = (results: number, defaultResults: number, max: number) => { - if (results < 1) { - return 1; - } - - if (results > max) { - return max; - } - - return results ?? defaultResults; -}; - -export function tracking(event: string, state?: MetricsModalState | null, metric?: string, query?: PromVisualQuery) { - switch (event) { - case 'grafana_prom_metric_encycopedia_tracking': - reportInteraction(event, { - metric: metric, - hasMetadata: state?.hasMetadata, - totalMetricCount: state?.totalMetricCount, - fuzzySearchQuery: state?.fuzzySearchQuery, - fullMetaSearch: state?.fullMetaSearch, - selectedTypes: state?.selectedTypes, - useRegexSearch: state?.useBackend, - includeResultsWithoutMetadata: state?.includeNullMetadata, - }); - case 'grafana_prom_metric_encycopedia_disable_text_wrap_interaction': - reportInteraction(event, { - disableTextWrap: state?.disableTextWrap, - }); - case 'grafana_prometheus_metric_encyclopedia_open': - reportInteraction(event, { - query: query, - }); - } -} - -export const getPromTypes: () => PromFilterOption[] = () => [ - { - value: 'counter', - label: t('grafana-prometheus.querybuilder.get-prom-types.label-counter', 'Counter'), - description: t( - 'grafana-prometheus.querybuilder.get-prom-types.description-counter', - 'A cumulative metric that represents a single monotonically increasing counter whose value can only increase or be reset to zero on restart.' - ), - }, - { - value: 'gauge', - label: t('grafana-prometheus.querybuilder.get-prom-types.label-gauge', 'Gauge'), - description: t( - 'grafana-prometheus.querybuilder.get-prom-types.description-gauge', - 'A metric that represents a single numerical value that can arbitrarily go up and down.' - ), - }, - { - value: 'histogram', - label: t('grafana-prometheus.querybuilder.get-prom-types.label-histogram', 'Histogram'), - description: t( - 'grafana-prometheus.querybuilder.get-prom-types.description-histogram', - 'A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets.' - ), - }, - { - value: 'native histogram', - label: t('grafana-prometheus.querybuilder.get-prom-types.label-native-histogram', 'Native histogram'), - description: t( - 'grafana-prometheus.querybuilder.get-prom-types.description-native-histogram', - 'Native histograms are different from classic Prometheus histograms in a number of ways: Native histogram bucket boundaries are calculated by a formula that depends on the scale (resolution) of the native histogram, and are not user defined.' - ), - }, - { - value: 'summary', - label: t('grafana-prometheus.querybuilder.get-prom-types.label-summary', 'Summary'), - description: t( - 'grafana-prometheus.querybuilder.get-prom-types.description-summary', - 'A summary samples observations (usually things like request durations and response sizes) and can calculate configurable quantiles over a sliding time window.' - ), - }, - { - value: 'unknown', - label: t('grafana-prometheus.querybuilder.get-prom-types.label-unknown', 'Unknown'), - description: t( - 'grafana-prometheus.querybuilder.get-prom-types.description-unknown', - 'These metrics have been given the type unknown in the metadata.' - ), - }, - { - value: 'no type', - label: t('grafana-prometheus.querybuilder.get-prom-types.label-no-type', 'No type'), - description: t( - 'grafana-prometheus.querybuilder.get-prom-types.description-no-type', - 'These metrics have no defined type in the metadata.' - ), - }, -]; - -export const getPlaceholders = () => ({ - browse: t('grafana-prometheus.querybuilder.get-placeholders.browse', 'Search metrics by name'), - metadataSearchSwitch: t( - 'grafana-prometheus.querybuilder.get-placeholders.metadata-search-switch', - 'Include description in search' - ), - type: t('grafana-prometheus.querybuilder.get-placeholders.type', 'Filter by type'), - includeNullMetadata: t( - 'grafana-prometheus.querybuilder.get-placeholders.include-null-metadata', - 'Include results with no metadata' - ), - setUseBackend: t('grafana-prometheus.querybuilder.get-placeholders.set-use-backend', 'Enable regex search'), -}); diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/state/state.ts b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/state/state.ts deleted file mode 100644 index 05031eee4db..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/state/state.ts +++ /dev/null @@ -1,212 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/state/state.ts -import { PayloadAction, createSlice } from '@reduxjs/toolkit'; - -import { SelectableValue } from '@grafana/data'; - -import { PromVisualQuery } from '../../../types'; -import { HaystackDictionary, MetricsData } from '../types'; - -export const DEFAULT_RESULTS_PER_PAGE = 100; -export const MAXIMUM_RESULTS_PER_PAGE = 1000; - -/** - * Initial state for the metrics explorer - * @returns - */ -export function initialState(query?: PromVisualQuery): MetricsModalState { - return { - isLoading: true, - metrics: [], - hasMetadata: true, - metaHaystackDictionary: {}, - metaHaystackMatches: [], - metaHaystackOrder: [], - nameHaystackDictionary: {}, - nameHaystackOrder: [], - nameHaystackMatches: [], - totalMetricCount: 0, - filteredMetricCount: null, - resultsPerPage: DEFAULT_RESULTS_PER_PAGE, - pageNum: 1, - fuzzySearchQuery: '', - fullMetaSearch: query?.fullMetaSearch ?? false, - includeNullMetadata: query?.includeNullMetadata ?? true, - selectedTypes: [], - useBackend: query?.useBackend ?? false, - disableTextWrap: query?.disableTextWrap ?? false, - showAdditionalSettings: false, - }; -} - -/** - * The metrics explorer state object - */ -export interface MetricsModalState { - /** Used for the loading spinner */ - isLoading: boolean; - /** - * Initial collection of metrics. - * The frontend filters do not impact this, but - * it is reduced by the backend search. - */ - metrics: MetricsData; - /** Field for disabling type select and switches that rely on metadata */ - hasMetadata: boolean; - /** Used to display metrics and help with fuzzy order */ - nameHaystackDictionary: HaystackDictionary; - /** Used to sort name fuzzy search by relevance */ - nameHaystackOrder: string[]; - /** Used to highlight text in fuzzy matches */ - nameHaystackMatches: string[]; - /** Used to display metrics and help with fuzzy order for search across all metadata */ - metaHaystackDictionary: HaystackDictionary; - /** Used to sort meta fuzzy search by relevance */ - metaHaystackOrder: string[]; - /** Used to highlight text in fuzzy matches */ - metaHaystackMatches: string[]; - /** Total results computed on initialization */ - totalMetricCount: number; - /** Set after filtering metrics */ - filteredMetricCount: number | null; - /** Pagination field for showing results in table */ - resultsPerPage: number; - /** Pagination field */ - pageNum: number; - /** The text query used to match metrics */ - fuzzySearchQuery: string; - /** Enables the fuzzy meatadata search */ - fullMetaSearch: boolean; - /** Includes results that are missing type and description */ - includeNullMetadata: boolean; - /** Filter by prometheus type */ - selectedTypes: Array>; - /** Filter by the series match endpoint instead of the fuzzy search */ - useBackend: boolean; - /** Disable text wrap for descriptions in the results table */ - disableTextWrap: boolean; - /** Display toggle switches for settings */ - showAdditionalSettings: boolean; -} - -/** - * Type for the useEffect get metadata function - */ -export type MetricsModalMetadata = { - isLoading: boolean; - metrics: MetricsData; - hasMetadata: boolean; - metaHaystackDictionary: HaystackDictionary; - nameHaystackDictionary: HaystackDictionary; - totalMetricCount: number; - filteredMetricCount: number | null; -}; - -// for updating the settings in the PromQuery model -export function getSettings(visQuery: PromVisualQuery): MetricsModalSettings { - return { - useBackend: visQuery?.useBackend ?? false, - disableTextWrap: visQuery?.disableTextWrap ?? false, - fullMetaSearch: visQuery?.fullMetaSearch ?? false, - includeNullMetadata: visQuery.includeNullMetadata ?? false, - }; -} - -export type MetricsModalSettings = { - useBackend?: boolean; - disableTextWrap?: boolean; - fullMetaSearch?: boolean; - includeNullMetadata?: boolean; -}; - -export const stateSlice = createSlice({ - name: 'metrics-modal-state', - initialState: initialState(), - reducers: { - filterMetricsBackend: ( - state, - action: PayloadAction<{ - metrics: MetricsData; - filteredMetricCount: number; - isLoading: boolean; - }> - ) => { - state.metrics = action.payload.metrics; - state.filteredMetricCount = action.payload.filteredMetricCount; - state.isLoading = action.payload.isLoading; - }, - buildMetrics: (state, action: PayloadAction) => { - state.isLoading = action.payload.isLoading; - state.metrics = action.payload.metrics; - state.hasMetadata = action.payload.hasMetadata; - state.metaHaystackDictionary = action.payload.metaHaystackDictionary; - state.nameHaystackDictionary = action.payload.nameHaystackDictionary; - state.totalMetricCount = action.payload.totalMetricCount; - state.filteredMetricCount = action.payload.filteredMetricCount; - }, - setIsLoading: (state, action: PayloadAction) => { - state.isLoading = action.payload; - }, - setFilteredMetricCount: (state, action: PayloadAction) => { - state.filteredMetricCount = action.payload; - }, - setResultsPerPage: (state, action: PayloadAction) => { - state.resultsPerPage = action.payload; - }, - setPageNum: (state, action: PayloadAction) => { - state.pageNum = action.payload; - }, - setFuzzySearchQuery: (state, action: PayloadAction) => { - state.fuzzySearchQuery = action.payload; - state.pageNum = 1; - }, - setNameHaystack: (state, action: PayloadAction) => { - state.nameHaystackOrder = action.payload[0]; - state.nameHaystackMatches = action.payload[1]; - }, - setMetaHaystack: (state, action: PayloadAction) => { - state.metaHaystackOrder = action.payload[0]; - state.metaHaystackMatches = action.payload[1]; - }, - setFullMetaSearch: (state, action: PayloadAction) => { - state.fullMetaSearch = action.payload; - state.pageNum = 1; - }, - setIncludeNullMetadata: (state, action: PayloadAction) => { - state.includeNullMetadata = action.payload; - state.pageNum = 1; - }, - setSelectedTypes: (state, action: PayloadAction>>) => { - state.selectedTypes = action.payload; - state.pageNum = 1; - }, - setUseBackend: (state, action: PayloadAction) => { - state.useBackend = action.payload; - state.fullMetaSearch = false; - state.pageNum = 1; - }, - setDisableTextWrap: (state) => { - state.disableTextWrap = !state.disableTextWrap; - }, - showAdditionalSettings: (state) => { - state.showAdditionalSettings = !state.showAdditionalSettings; - }, - }, -}); - -export const { - setIsLoading, - buildMetrics, - filterMetricsBackend, - setResultsPerPage, - setPageNum, - setFuzzySearchQuery, - setNameHaystack, - setMetaHaystack, - setFullMetaSearch, - setIncludeNullMetadata, - setSelectedTypes, - setUseBackend, - setDisableTextWrap, - showAdditionalSettings, - setFilteredMetricCount, -} = stateSlice.actions; diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/styles.ts b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/styles.ts index bcc6fa425c7..64e52600055 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/styles.ts +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/styles.ts @@ -3,7 +3,7 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; -export const getStyles = (theme: GrafanaTheme2, disableTextWrap: boolean) => { +export const getMetricsModalStyles = (theme: GrafanaTheme2) => { return { modal: css({ width: '85vw', @@ -34,20 +34,9 @@ export const getStyles = (theme: GrafanaTheme2, disableTextWrap: boolean) => { minWidth: '100%', }, }), - selectWrapper: css({ - marginBottom: theme.spacing(1), - }), - resultsAmount: css({ - color: theme.colors.text.secondary, - fontSize: '0.85rem', - padding: '0 0 4px 0', - }), resultsData: css({ margin: `4px 0 ${theme.spacing(2)} 0`, }), - resultsDataCount: css({ - margin: 0, - }), resultsDataFiltered: css({ color: theme.colors.text.secondary, textAlign: 'center', @@ -67,9 +56,9 @@ export const getStyles = (theme: GrafanaTheme2, disableTextWrap: boolean) => { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', - justifyContent: 'space-between', alignItems: 'center', position: 'sticky', + justifyContent: 'center', }), currentlySelected: css({ color: 'grey', @@ -82,21 +71,69 @@ export const getStyles = (theme: GrafanaTheme2, disableTextWrap: boolean) => { visible: css({ visibility: 'visible', }), - settingsBtn: css({ - float: 'right', - }), noBorder: css({ border: 'none', }), - resultsPerPageLabel: css({ - color: theme.colors.text.secondary, - opacity: '75%', - paddingTop: '5px', - fontSize: '0.85rem', - marginRight: '8px', + }; +}; + +export const getResultsTableStyles = (theme: GrafanaTheme2) => { + return { + table: css({ + tableLayout: 'fixed', + borderRadius: theme.shape.radius.default, + width: '100%', + whiteSpace: 'normal', + td: { + padding: theme.spacing(1), + }, + 'td,th': { + minWidth: theme.spacing(3), + borderBottom: `1px solid ${theme.colors.border.weak}`, + }, }), - resultsPerPageWrapper: css({ - display: 'flex', + row: css({ + label: 'row', + borderBottom: `1px solid ${theme.colors.border.weak}`, + cursor: 'pointer', + '&:last-child': { + borderBottom: 0, + }, + '&:hover': { + backgroundColor: theme.colors.background.secondary, + }, + }), + tableHeaderPadding: css({ + padding: '8px', + }), + matchHighLight: css({ + background: 'inherit', + color: theme.components.textHighlight.text, + backgroundColor: theme.components.textHighlight.background, + }), + nameWidth: css({ + width: '37.5%', + }), + nameOverflow: css({ + overflowWrap: 'anywhere', + }), + typeWidth: css({ + width: '15%', + }), + descriptionWidth: css({ + width: '35%', + }), + stickyHeader: css({ + position: 'sticky', + top: 0, + backgroundColor: theme.colors.background.primary, + }), + noResults: css({ + textAlign: 'center', + color: theme.colors.text.secondary, + }), + tooltipSpace: css({ + marginLeft: '4px', }), }; }; diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/shared/testIds.ts b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/testIds.ts similarity index 65% rename from packages/grafana-prometheus/src/querybuilder/components/metrics-modal/shared/testIds.ts rename to packages/grafana-prometheus/src/querybuilder/components/metrics-modal/testIds.ts index 275394dd348..f1e5c7709e8 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/shared/testIds.ts +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/testIds.ts @@ -1,12 +1,9 @@ export const metricsModaltestIds = { metricModal: 'metric-modal', searchMetric: 'search-metric', - searchWithMetadata: 'search-with-metadata', selectType: 'select-type', metricCard: 'metric-card', useMetric: 'use-metric', searchPage: 'search-page', resultsPerPage: 'results-per-page', - setUseBackend: 'set-use-backend', - showAdditionalSettings: 'show-additional-settings', }; diff --git a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/uFuzzy.ts b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/uFuzzy.ts index 297e216b560..3de9b6f1828 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/uFuzzy.ts +++ b/packages/grafana-prometheus/src/querybuilder/components/metrics-modal/uFuzzy.ts @@ -1,6 +1,5 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/metrics-modal/uFuzzy.ts import uFuzzy from '@leeoniya/ufuzzy'; -import { debounce as debounceLodash } from 'lodash'; const uf = new uFuzzy({ intraMode: 1, @@ -10,37 +9,34 @@ const uf = new uFuzzy({ intraDel: 1, }); -function fuzzySearch(haystack: string[], query: string, dispatcher: (data: string[][]) => void) { +export const fuzzySearch = (haystack: string[], query: string) => { const [idxs, info, order] = uf.search(haystack, query, 0, 1e5); let haystackOrder: string[] = []; let matchesSet: Set = new Set(); - if (idxs && order) { - /** - * get the fuzzy matches for hilighting - * @param part - * @param matched - */ - const mark = (part: string, matched: boolean) => { - if (matched) { - matchesSet.add(part); - } - }; - - // Iterate to create the order of needles(queries) and the matches - for (let i = 0; i < order.length; i++) { - let infoIdx = order[i]; - - /** Evaluate the match, get the matches for highlighting */ - uFuzzy.highlight(haystack[info.idx[infoIdx]], info.ranges[infoIdx], mark); - /** Get the order */ - haystackOrder.push(haystack[info.idx[infoIdx]]); - } - - dispatcher([haystackOrder, [...matchesSet]]); - } else if (!query) { - dispatcher([[], []]); + if (!(idxs && order)) { + return [[], []]; } -} + /** + * get the fuzzy matches for highlighting + * @param part + * @param matched + */ + const mark = (part: string, matched: boolean) => { + if (matched) { + matchesSet.add(part); + } + }; -export const debouncedFuzzySearch = debounceLodash(fuzzySearch, 300); + // Iterate to create the order of needles(queries) and the matches + for (let i = 0; i < order.length; i++) { + let infoIdx = order[i]; + + /** Evaluate the match, get the matches for highlighting */ + uFuzzy.highlight(haystack[info.idx[infoIdx]], info.ranges[infoIdx], mark); + /** Get the order */ + haystackOrder.push(haystack[info.idx[infoIdx]]); + } + + return [haystackOrder, [...matchesSet]]; +}; diff --git a/packages/grafana-prometheus/src/querybuilder/components/shared/BaseQueryBuilder.tsx b/packages/grafana-prometheus/src/querybuilder/components/shared/BaseQueryBuilder.tsx deleted file mode 100644 index 03787aad88a..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/shared/BaseQueryBuilder.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { memo } from 'react'; - -import { NestedQueryList } from '../NestedQueryList'; - -import { QueryBuilderContent } from './QueryBuilderContent'; -import { BaseQueryBuilderProps } from './types'; - -export const BaseQueryBuilder = memo((props) => { - const { query, datasource, onChange, onRunQuery, showExplain } = props; - - return ( - <> - - {query.binaryQueries && query.binaryQueries.length > 0 && ( - - )} - - ); -}); - -BaseQueryBuilder.displayName = 'BaseQueryBuilder'; diff --git a/packages/grafana-prometheus/src/querybuilder/components/shared/types.ts b/packages/grafana-prometheus/src/querybuilder/components/shared/types.ts deleted file mode 100644 index 626e6606259..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/shared/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { PanelData } from '@grafana/data'; - -import { PrometheusDatasource } from '../../../datasource'; -import { PromVisualQuery } from '../../types'; - -export interface BaseQueryBuilderProps { - query: PromVisualQuery; - datasource: PrometheusDatasource; - onChange: (update: PromVisualQuery) => void; - onRunQuery: () => void; - data?: PanelData; - showExplain: boolean; -} diff --git a/packages/grafana-prometheus/src/querybuilder/types.ts b/packages/grafana-prometheus/src/querybuilder/types.ts index 991cfec8969..c2bc18bc2e5 100644 --- a/packages/grafana-prometheus/src/querybuilder/types.ts +++ b/packages/grafana-prometheus/src/querybuilder/types.ts @@ -14,11 +14,6 @@ export interface PromVisualQuery { labels: QueryBuilderLabelFilter[]; operations: QueryBuilderOperation[]; binaryQueries?: PromVisualQueryBinary[]; - // metrics explorer additional settings - useBackend?: boolean; - disableTextWrap?: boolean; - includeNullMetadata?: boolean; - fullMetaSearch?: boolean; } export interface PromQueryModellerInterface { diff --git a/packages/grafana-prometheus/src/types.ts b/packages/grafana-prometheus/src/types.ts index acd5b803ee2..98730018bf3 100644 --- a/packages/grafana-prometheus/src/types.ts +++ b/packages/grafana-prometheus/src/types.ts @@ -15,11 +15,6 @@ export interface PromQuery extends GenPromQuery, DataQuery { showingTable?: boolean; hinting?: boolean; interval?: string; - // store the metrics explorer additional settings - useBackend?: boolean; - disableTextWrap?: boolean; - fullMetaSearch?: boolean; - includeNullMetadata?: boolean; fromExploreMetrics?: boolean; } diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index f32cc4639da..f3506105902 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -70,7 +70,7 @@ "@grafana/tsconfig": "^2.0.0", "@rollup/plugin-node-resolve": "16.0.1", "@rollup/plugin-terser": "0.4.4", - "@testing-library/dom": "10.4.0", + "@testing-library/dom": "10.4.1", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", "@types/history": "4.7.11", @@ -87,7 +87,7 @@ "rollup-plugin-esbuild": "6.2.1", "rollup-plugin-node-externals": "^8.0.0", "rollup-plugin-sourcemaps": "0.6.3", - "typescript": "5.8.3" + "typescript": "5.9.2" }, "peerDependencies": { "react": "^18.0.0", diff --git a/packages/grafana-schema/package.json b/packages/grafana-schema/package.json index 9896376d716..ef7cc40d4c5 100644 --- a/packages/grafana-schema/package.json +++ b/packages/grafana-schema/package.json @@ -44,7 +44,7 @@ "rollup": "^4.22.4", "rollup-plugin-esbuild": "6.2.1", "rollup-plugin-node-externals": "^8.0.0", - "typescript": "5.8.3" + "typescript": "5.9.2" }, "dependencies": { "tslib": "2.8.1" diff --git a/packages/grafana-schema/src/common/common.gen.ts b/packages/grafana-schema/src/common/common.gen.ts index b98c83c9525..c256e8ed6bd 100644 --- a/packages/grafana-schema/src/common/common.gen.ts +++ b/packages/grafana-schema/src/common/common.gen.ts @@ -708,6 +708,7 @@ export enum TableCellDisplayMode { Image = 'image', JSONView = 'json-view', LcdGauge = 'lcd-gauge', + Markdown = 'markdown', Pill = 'pill', Sparkline = 'sparkline', } @@ -722,6 +723,16 @@ export enum TableCellBackgroundDisplayMode { Gradient = 'gradient', } +/** + * Whenever we add text wrapping, we should add all text wrapping options at once + */ +export interface TableWrapTextOptions { + /** + * if true, wrap the text content of the cell + */ + wrapText?: boolean; +} + /** * Sort by field state */ @@ -755,17 +766,15 @@ export const defaultTableFooterOptions: Partial = { /** * Auto mode table cell options */ -export interface TableAutoCellOptions { +export interface TableAutoCellOptions extends TableWrapTextOptions { type: TableCellDisplayMode.Auto; - wrapText?: boolean; } /** * Colored text cell options */ -export interface TableColorTextCellOptions { +export interface TableColorTextCellOptions extends TableWrapTextOptions { type: TableCellDisplayMode.ColorText; - wrapText?: boolean; } /** @@ -787,7 +796,7 @@ export interface TableImageCellOptions { /** * Show data links in the cell */ -export interface TableDataLinksCellOptions { +export interface TableDataLinksCellOptions extends TableWrapTextOptions { type: TableCellDisplayMode.DataLinks; } @@ -818,11 +827,19 @@ export interface TableSparklineCellOptions extends GraphFieldConfig { /** * Colored background cell options */ -export interface TableColoredBackgroundCellOptions { +export interface TableColoredBackgroundCellOptions extends TableWrapTextOptions { applyToRow?: boolean; mode?: TableCellBackgroundDisplayMode; type: TableCellDisplayMode.ColorBackground; - wrapText?: boolean; +} + +export interface TablePillCellOptions extends TableWrapTextOptions { + type: TableCellDisplayMode.Pill; +} + +export interface TableMarkdownCellOptions { + dynamicHeight?: boolean; + type: TableCellDisplayMode.Markdown; } /** @@ -839,38 +856,7 @@ export enum TableCellHeight { * Table cell options. Each cell has a display mode * and other potential options for that display. */ -export type TableCellOptions = (TableAutoCellOptions | TableSparklineCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions | TableColorTextCellOptions | TableImageCellOptions | TablePillCellOptions | TableDataLinksCellOptions | TableActionsCellOptions | TableJsonViewCellOptions); - -/** - * Field options for each field within a table (e.g 10, "The String", 64.20, etc.) - * Generally defines alignment, filtering capabilties, display options, etc. - */ -export interface TableFieldOptions { - align: FieldTextAlignment; - cellOptions: TableCellOptions; - /** - * This field is deprecated in favor of using cellOptions - */ - displayMode?: TableCellDisplayMode; - filterable?: boolean; - hidden?: boolean; // ?? default is missing or false ?? - /** - * Hides any header for a column, useful for columns that show some static content or buttons. - */ - hideHeader?: boolean; - inspect: boolean; - minWidth?: number; - width?: number; - /** - * Enables text wrapping for column headers - */ - wrapHeaderText?: boolean; -} - -export const defaultTableFieldOptions: Partial = { - align: 'auto', - inspect: false, -}; +export type TableCellOptions = (TableAutoCellOptions | TableSparklineCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions | TableColorTextCellOptions | TableImageCellOptions | TablePillCellOptions | TableDataLinksCellOptions | TableActionsCellOptions | TableJsonViewCellOptions | TableMarkdownCellOptions); /** * Use UTC/GMT timezone @@ -987,10 +973,37 @@ export enum ComparisonOperation { NEQ = 'neq', } -export interface TablePillCellOptions { - type: TableCellDisplayMode.Pill; +/** + * Field options for each field within a table (e.g 10, "The String", 64.20, etc.) + * Generally defines alignment, filtering capabilties, display options, etc. + */ +export interface TableFieldOptions { + align: FieldTextAlignment; + cellOptions: TableCellOptions; + /** + * This field is deprecated in favor of using cellOptions + */ + displayMode?: TableCellDisplayMode; + filterable?: boolean; + hidden?: boolean; // ?? default is missing or false ?? + /** + * Hides any header for a column, useful for columns that show some static content or buttons. + */ + hideHeader?: boolean; + inspect: boolean; + minWidth?: number; + width?: number; + /** + * Enables text wrapping for column headers + */ + wrapHeaderText?: boolean; } +export const defaultTableFieldOptions: Partial = { + align: 'auto', + inspect: false, +}; + /** * A specific timezone from https://en.wikipedia.org/wiki/Tz_database */ diff --git a/packages/grafana-schema/src/common/table.cue b/packages/grafana-schema/src/common/table.cue index acf4a6b7480..57b7b56dc44 100644 --- a/packages/grafana-schema/src/common/table.cue +++ b/packages/grafana-schema/src/common/table.cue @@ -4,13 +4,19 @@ package common // in the table such as colored text, JSON, gauge, etc. // The color-background-solid, gradient-gauge, and lcd-gauge // modes are deprecated in favor of new cell subOptions -TableCellDisplayMode: "auto" | "color-text" | "color-background" | "color-background-solid" | "gradient-gauge" | "lcd-gauge" | "json-view" | "basic" | "image" | "gauge" | "sparkline" | "data-links" | "custom" | "actions" | "pill" @cuetsy(kind="enum",memberNames="Auto|ColorText|ColorBackground|ColorBackgroundSolid|GradientGauge|LcdGauge|JSONView|BasicGauge|Image|Gauge|Sparkline|DataLinks|Custom|Actions|Pill") +TableCellDisplayMode: "auto" | "color-text" | "color-background" | "color-background-solid" | "gradient-gauge" | "lcd-gauge" | "json-view" | "basic" | "image" | "gauge" | "sparkline" | "data-links" | "custom" | "actions" | "pill" | "markdown" @cuetsy(kind="enum",memberNames="Auto|ColorText|ColorBackground|ColorBackgroundSolid|GradientGauge|LcdGauge|JSONView|BasicGauge|Image|Gauge|Sparkline|DataLinks|Custom|Actions|Pill|Markdown") // Display mode to the "Colored Background" display // mode for table cells. Either displays a solid color (basic mode) // or a gradient. TableCellBackgroundDisplayMode: "basic" | "gradient" @cuetsy(kind="enum",memberNames="Basic|Gradient") +// Whenever we add text wrapping, we should add all text wrapping options at once +TableWrapTextOptions: { + // if true, wrap the text content of the cell + wrapText?: bool +} @cuetsy(kind="interface") + // Sort by field state TableSortByFieldState: { // Sets the display name of the field to sort by @@ -31,14 +37,12 @@ TableFooterOptions: { // Auto mode table cell options TableAutoCellOptions: { type: TableCellDisplayMode & "auto" - wrapText?: bool -} @cuetsy(kind="interface") +} & TableWrapTextOptions @cuetsy(kind="interface") // Colored text cell options TableColorTextCellOptions: { type: TableCellDisplayMode & "color-text" - wrapText?: bool -} @cuetsy(kind="interface") +} & TableWrapTextOptions @cuetsy(kind="interface") // Json view cell options TableJsonViewCellOptions: { @@ -55,7 +59,7 @@ TableImageCellOptions: { // Show data links in the cell TableDataLinksCellOptions: { type: TableCellDisplayMode & "data-links" -} @cuetsy(kind="interface") +} & TableWrapTextOptions @cuetsy(kind="interface") // Show actions in the cell TableActionsCellOptions: { @@ -81,7 +85,15 @@ TableColoredBackgroundCellOptions: { type: TableCellDisplayMode & "color-background" mode?: TableCellBackgroundDisplayMode applyToRow?: bool - wrapText?: bool +} & TableWrapTextOptions @cuetsy(kind="interface") + +TablePillCellOptions: { + type: TableCellDisplayMode & "pill" +} & TableWrapTextOptions @cuetsy(kind="interface") + +TableMarkdownCellOptions: { + type: TableCellDisplayMode & "markdown" + dynamicHeight?: bool } @cuetsy(kind="interface") // Height of a table cell @@ -89,7 +101,7 @@ TableCellHeight: "sm" | "md" | "lg" | "auto" @cuetsy(kind="enum") // Table cell options. Each cell has a display mode // and other potential options for that display. -TableCellOptions: TableAutoCellOptions | TableSparklineCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions | TableColorTextCellOptions | TableImageCellOptions | TablePillCellOptions | TableDataLinksCellOptions | TableActionsCellOptions | TableJsonViewCellOptions @cuetsy(kind="type") +TableCellOptions: TableAutoCellOptions | TableSparklineCellOptions | TableBarGaugeCellOptions | TableColoredBackgroundCellOptions | TableColorTextCellOptions | TableImageCellOptions | TablePillCellOptions | TableDataLinksCellOptions | TableActionsCellOptions | TableJsonViewCellOptions | TableMarkdownCellOptions @cuetsy(kind="type") // Field options for each field within a table (e.g 10, "The String", 64.20, etc.) // Generally defines alignment, filtering capabilties, display options, etc. @@ -108,7 +120,3 @@ TableFieldOptions: { // Enables text wrapping for column headers wrapHeaderText?: bool } @cuetsy(kind="interface") - -TablePillCellOptions: { - type: TableCellDisplayMode & "pill" -} @cuetsy(kind="interface") diff --git a/packages/grafana-sql/package.json b/packages/grafana-sql/package.json index e99c2f9a737..6b2ab84c1dc 100644 --- a/packages/grafana-sql/package.json +++ b/packages/grafana-sql/package.json @@ -37,7 +37,7 @@ }, "devDependencies": { "@grafana/tsconfig": "^2.0.0", - "@testing-library/dom": "10.4.0", + "@testing-library/dom": "10.4.1", "@testing-library/jest-dom": "^6.1.2", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", @@ -53,7 +53,7 @@ "jest": "^29.6.4", "ts-jest": "29.4.0", "ts-node": "10.9.2", - "typescript": "5.8.3" + "typescript": "5.9.2" }, "peerDependencies": { "@grafana/runtime": "10.4.0-pre" diff --git a/packages/grafana-test-utils/package.json b/packages/grafana-test-utils/package.json index fe04697966f..99eedba93d4 100644 --- a/packages/grafana-test-utils/package.json +++ b/packages/grafana-test-utils/package.json @@ -62,6 +62,6 @@ "@types/jest": "29.5.14", "@types/node": "22.17.0", "jest": "29.7.0", - "typescript": "5.8.3" + "typescript": "5.9.2" } } diff --git a/packages/grafana-test-utils/src/fixtures/folders.ts b/packages/grafana-test-utils/src/fixtures/folders.ts new file mode 100644 index 00000000000..7fc9bed9e20 --- /dev/null +++ b/packages/grafana-test-utils/src/fixtures/folders.ts @@ -0,0 +1,130 @@ +import { Chance } from 'chance'; + +import { DashboardsTreeItem, DashboardViewItem, UIDashboardViewItem } from '../types/browse-dashboards'; + +function wellFormedEmptyFolder( + seed = 1, + partial?: Partial> +): DashboardsTreeItem { + const random = Chance(seed); + + return { + item: { + kind: 'ui', + uiKind: 'empty-folder', + uid: random.guid(), + }, + level: 0, + isOpen: false, + ...partial, + }; +} + +function wellFormedDashboard( + seed = 1, + partial?: Partial>, + itemPartial?: Partial +): DashboardsTreeItem { + const random = Chance(seed); + + return { + item: { + kind: 'dashboard', + title: random.sentence({ words: 3 }), + uid: random.guid(), + tags: [random.word()], + ...itemPartial, + }, + level: 0, + isOpen: false, + ...partial, + }; +} + +function wellFormedFolder( + seed = 1, + partial?: Partial>, + itemPartial?: Partial +): DashboardsTreeItem { + const random = Chance(seed); + const uid = random.guid(); + + return { + item: { + kind: 'folder', + title: random.sentence({ words: 3 }), + uid, + url: `/dashboards/f/${uid}`, + ...itemPartial, + }, + level: 0, + isOpen: false, + ...partial, + }; +} + +export function treeViewersCanEdit() { + const [, { folderA, folderC }] = wellFormedTree(); + + return [ + [folderA, folderC], + { + folderA, + folderC, + }, + ] as const; +} + +export function wellFormedTree() { + let seed = 1; + + const folderA = wellFormedFolder(seed++); + const folderA_folderA = wellFormedFolder(seed++, { level: 1 }, { parentUID: folderA.item.uid }); + const folderA_folderB = wellFormedFolder(seed++, { level: 1 }, { parentUID: folderA.item.uid }); + const folderA_folderB_dashbdA = wellFormedDashboard(seed++, { level: 2 }, { parentUID: folderA_folderB.item.uid }); + const folderA_folderB_dashbdB = wellFormedDashboard(seed++, { level: 2 }, { parentUID: folderA_folderB.item.uid }); + const folderA_folderC = wellFormedFolder(seed++, { level: 1 }, { parentUID: folderA.item.uid }); + const folderA_folderC_dashbdA = wellFormedDashboard(seed++, { level: 2 }, { parentUID: folderA_folderC.item.uid }); + const folderA_folderC_dashbdB = wellFormedDashboard(seed++, { level: 2 }, { parentUID: folderA_folderC.item.uid }); + const folderA_dashbdD = wellFormedDashboard(seed++, { level: 1 }, { parentUID: folderA.item.uid }); + const folderB = wellFormedFolder(seed++); + const folderB_empty = wellFormedEmptyFolder(seed++); + const folderC = wellFormedFolder(seed++); + const dashbdD = wellFormedDashboard(seed++); + const dashbdE = wellFormedDashboard(seed++); + + return [ + [ + folderA, + folderA_folderA, + folderA_folderB, + folderA_folderB_dashbdA, + folderA_folderB_dashbdB, + folderA_folderC, + folderA_folderC_dashbdA, + folderA_folderC_dashbdB, + folderA_dashbdD, + folderB, + folderB_empty, + folderC, + dashbdD, + dashbdE, + ], + { + folderA, + folderA_folderA, + folderA_folderB, + folderA_folderB_dashbdA, + folderA_folderB_dashbdB, + folderA_folderC, + folderA_folderC_dashbdA, + folderA_folderC_dashbdB, + folderA_dashbdD, + folderB, + folderB_empty, + folderC, + dashbdD, + dashbdE, + }, + ] as const; +} diff --git a/packages/grafana-test-utils/src/handlers/all-handlers.ts b/packages/grafana-test-utils/src/handlers/all-handlers.ts index edf4c592ef2..3e626ba021b 100644 --- a/packages/grafana-test-utils/src/handlers/all-handlers.ts +++ b/packages/grafana-test-utils/src/handlers/all-handlers.ts @@ -1,7 +1,9 @@ import { HttpHandler } from 'msw'; +import folderHandlers from './api/folders/handlers'; import teamsHandlers from './api/teams/handlers'; +import appPlatformFolderHandlers from './apis/dashboard.grafana.app/v0alpha1/handlers'; -const allHandlers: HttpHandler[] = [...teamsHandlers]; +const allHandlers: HttpHandler[] = [...teamsHandlers, ...folderHandlers, ...appPlatformFolderHandlers]; export default allHandlers; diff --git a/packages/grafana-test-utils/src/handlers/api/folders/handlers.ts b/packages/grafana-test-utils/src/handlers/api/folders/handlers.ts new file mode 100644 index 00000000000..103b816ce3a --- /dev/null +++ b/packages/grafana-test-utils/src/handlers/api/folders/handlers.ts @@ -0,0 +1,52 @@ +import { HttpResponse, http } from 'msw'; + +import { treeViewersCanEdit, wellFormedTree } from '../../../fixtures/folders'; + +const [mockTree] = wellFormedTree(); +const [mockTreeThatViewersCanEdit] = treeViewersCanEdit(); +const collator = new Intl.Collator(); + +const listFoldersHandler = () => + http.get('/api/folders', ({ request }) => { + const url = new URL(request.url); + const parentUid = url.searchParams.get('parentUid') ?? undefined; + const permission = url.searchParams.get('permission'); + + const limit = parseInt(url.searchParams.get('limit') ?? '1000', 10); + const page = parseInt(url.searchParams.get('page') ?? '1', 10); + + const tree = permission === 'Edit' ? mockTreeThatViewersCanEdit : mockTree; + + // reconstruct a folder API response from the flat tree fixture + const folders = tree + .filter((v) => v.item.kind === 'folder' && v.item.parentUID === parentUid) + .map((folder) => { + return { + uid: folder.item.uid, + title: folder.item.kind === 'folder' ? folder.item.title : "invalid - this shouldn't happen", + }; + }) + .sort((a, b) => collator.compare(a.title, b.title)) // API always sorts by title + .slice(limit * (page - 1), limit * page); + + return HttpResponse.json(folders); + }); + +const getFolderHandler = () => + http.get('/api/folders/:uid', ({ params }) => { + const { uid } = params; + + const folder = mockTree.find((v) => v.item.uid === uid); + if (!folder) { + return HttpResponse.json({ message: 'folder not found', status: 'not-found' }, { status: 404 }); + } + + return HttpResponse.json({ + title: folder?.item.title, + uid: folder?.item.uid, + }); + }); + +const handlers = [listFoldersHandler(), getFolderHandler()]; + +export default handlers; diff --git a/packages/grafana-test-utils/src/handlers/apis/dashboard.grafana.app/v0alpha1/handlers.ts b/packages/grafana-test-utils/src/handlers/apis/dashboard.grafana.app/v0alpha1/handlers.ts new file mode 100644 index 00000000000..43813d6f4f8 --- /dev/null +++ b/packages/grafana-test-utils/src/handlers/apis/dashboard.grafana.app/v0alpha1/handlers.ts @@ -0,0 +1,52 @@ +import { Chance } from 'chance'; +import { HttpResponse, http } from 'msw'; + +import { wellFormedTree } from '../../../../fixtures/folders'; + +const [mockTree] = wellFormedTree(); + +type FilterArray = Array<(v: (typeof mockTree)[number]) => boolean>; + +const getSearchHandler = () => + http.get('/apis/dashboard.grafana.app/v0alpha1/namespaces/default/search', ({ request }) => { + const folderFilter = new URL(request.url).searchParams.get('folder') || null; + const typeFilter = new URL(request.url).searchParams.get('type') || null; + const response = mockTree + .filter((filterItem) => { + const filters: FilterArray = []; + if (folderFilter && folderFilter !== 'general') { + filters.push(({ item }) => item.kind === 'folder' && item.parentUID === folderFilter); + } + + if (folderFilter === 'general') { + filters.push(({ item }) => item.kind === 'folder' && item.parentUID === undefined); + } + + if (typeFilter) { + filters.push(({ item }) => item.kind === typeFilter); + } + + return filters.every((filterPredicate) => filterPredicate(filterItem)); + }) + + .map(({ item }) => { + const random = Chance(item.uid); + return { + resource: 'folders', + name: item.uid, + title: item.title, + field: { + // Generate mock deprecated IDs only in the mock handlers - not generating in + // mock data as it would require updating/tracking in the types as well + 'grafana.app/deprecatedInternalID': random.integer({ min: 1, max: 1000 }), + }, + }; + }); + + return HttpResponse.json({ + totalHits: response.length, + hits: response, + }); + }); + +export default [getSearchHandler()]; diff --git a/packages/grafana-test-utils/src/types/browse-dashboards.ts b/packages/grafana-test-utils/src/types/browse-dashboards.ts new file mode 100644 index 00000000000..5757b4a2ec0 --- /dev/null +++ b/packages/grafana-test-utils/src/types/browse-dashboards.ts @@ -0,0 +1,58 @@ +// FIXME: This file is a duplication of types within the core code +// Where should these live long term? +// @grafana/schema? +// New package @grafana/core? @grafana/types? + +enum ManagerKind { + Repo = 'repo', + Terraform = 'terraform', + Kubectl = 'kubectl', + Plugin = 'plugin', +} + +type DashboardViewItemKind = 'folder' | 'dashboard' | 'panel'; + +type DashboardViewItemWithUIItems = DashboardViewItem | UIDashboardViewItem; + +export interface DashboardsTreeItem { + item: T; + level: number; + isOpen: boolean; + parentUID?: string; +} + +export interface UIDashboardViewItem { + kind: 'ui'; + uiKind: 'empty-folder' | 'pagination-placeholder' | 'divider'; + uid: string; + // Optional title to make mock data easier to work with + title?: string; +} + +/** + * Type used in the folder view components + */ +export interface DashboardViewItem { + kind: DashboardViewItemKind; + uid: string; + title: string; + url?: string; + tags?: string[]; + + icon?: string; + + parentUID?: string; + /** @deprecated Not used in new Browse UI */ + parentTitle?: string; + /** @deprecated Not used in new Browse UI */ + parentKind?: string; + + // Used only for psuedo-folders, such as Starred or Recent + /** @deprecated Not used in new Browse UI */ + itemsUIDs?: string[]; + + // For enterprise sort options + sortMeta?: number | string; // value sorted by + sortMetaName?: string; // name of the value being sorted e.g. 'Views' + managedBy?: ManagerKind; +} diff --git a/packages/grafana-test-utils/src/unstable.ts b/packages/grafana-test-utils/src/unstable.ts index 5b60f83479c..8758e294464 100644 --- a/packages/grafana-test-utils/src/unstable.ts +++ b/packages/grafana-test-utils/src/unstable.ts @@ -1 +1,4 @@ +import { wellFormedTree } from './fixtures/folders'; + +export const getFolderFixtures = wellFormedTree; export { MOCK_TEAMS } from './fixtures/teams'; diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 2193c02d0e8..78f53868ba8 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -109,7 +109,7 @@ "react-calendar": "^6.0.0", "react-colorful": "5.6.1", "react-custom-scrollbars-2": "4.5.0", - "react-data-grid": "grafana/react-data-grid#de920f0105cb2b7d774444e7443a675f3b568ad6", + "react-data-grid": "grafana/react-data-grid#a922856b5ede21d55db3fdffb6d38dc76bdc7c58", "react-dropzone": "14.3.8", "react-highlight-words": "0.21.0", "react-hook-form": "^7.49.2", @@ -155,8 +155,8 @@ "@storybook/react-webpack5": "^8.6.2", "@storybook/test-runner": "^0.23.0", "@storybook/theming": "^8.6.2", - "@testing-library/dom": "10.4.0", - "@testing-library/jest-dom": "6.6.3", + "@testing-library/dom": "10.4.1", + "@testing-library/jest-dom": "6.6.4", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", "@types/chance": "^1.1.7", @@ -203,7 +203,7 @@ "sass-loader": "16.0.5", "storybook": "^8.6.2", "style-loader": "4.0.0", - "typescript": "5.8.3", + "typescript": "5.9.2", "webpack": "5.101.0" }, "peerDependencies": { diff --git a/packages/grafana-ui/src/components/Table/TableNG/Cells/MarkdownCell.tsx b/packages/grafana-ui/src/components/Table/TableNG/Cells/MarkdownCell.tsx new file mode 100644 index 00000000000..7d4922c2434 --- /dev/null +++ b/packages/grafana-ui/src/components/Table/TableNG/Cells/MarkdownCell.tsx @@ -0,0 +1,17 @@ +import { renderMarkdown } from '@grafana/data'; + +import { MaybeWrapWithLink } from '../MaybeWrapWithLink'; +import { MarkdownCellProps } from '../types'; + +export function MarkdownCell({ field, rowIdx, disableSanitizeHtml }: MarkdownCellProps) { + return ( + +
+ + ); +} diff --git a/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.test.tsx b/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.test.tsx index b7249e88b2b..55b9d3e19ba 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.test.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.test.tsx @@ -1,54 +1,18 @@ import { render, RenderResult } from '@testing-library/react'; -import { DataFrame, Field, FieldType, GrafanaTheme2, MappingType, createTheme } from '@grafana/data'; -import { TableCellDisplayMode, TablePillCellOptions } from '@grafana/schema'; +import { Field, FieldType, MappingType, createTheme } from '@grafana/data'; -import { mockThemeContext } from '../../../../themes/ThemeContext'; - -import { PillCell, getStyles } from './PillCell'; +import { PillCell } from './PillCell'; describe('PillCell', () => { - let pillClass: string; - let restoreThemeContext: () => void; + const theme = createTheme(); - beforeEach(() => { - pillClass = getStyles(createTheme()).pill; - restoreThemeContext = mockThemeContext(createTheme()); - }); - - afterEach(() => { - restoreThemeContext(); - }); - - const mockCellOptions: TablePillCellOptions = { - type: TableCellDisplayMode.Pill, - }; - - const mockField: Field = { + const fieldWithValues = (values: unknown[]): Field => ({ name: 'test', type: FieldType.string, - values: [], + values: values, config: {}, - }; - - const mockFrame: DataFrame = { - name: 'test', - fields: [mockField], - length: 1, - }; - - const defaultProps = { - field: mockField, - justifyContent: 'flex-start' as const, - cellOptions: mockCellOptions, - rowIdx: 0, - frame: mockFrame, - height: 30, - width: 100, - theme: {} as GrafanaTheme2, - cellInspect: false, - showFilters: false, - }; + }); const ser = new XMLSerializer(); @@ -60,88 +24,84 @@ describe('PillCell', () => { // one class for lightTextPill, darkTextPill describe('Color by hash (classic palette)', () => { - const props = { ...defaultProps }; - it('single value', () => { expectHTML( - render(), - `value1` + render(), + `value1` ); }); it('empty string', () => { - expectHTML(render(), ''); + expectHTML(render(), ''); }); - // it('null', () => { - // expectHTML( - // render(), - // 'value1' - // ); - // }); + it('null', () => { + const { container } = render(); + expect(container).toBeEmptyDOMElement(); + }); it('CSV values', () => { expectHTML( - render(), + render(), ` - value1 - value2 - value3 + value1 + value2 + value3 ` ); }); it('JSON array values', () => { expectHTML( - render(), + render(), ` - value1 - value2 - value3 + value1 + value2 + value3 ` ); }); - - // TODO: handle null values? }); describe('Color by value mappings', () => { - const field: Field = { - ...mockField, - config: { - ...mockField.config, - mappings: [ - { - type: MappingType.ValueToText, - options: { - success: { color: '#00FF00' }, - error: { color: '#FF0000' }, - warning: { color: '#FFFF00' }, - }, - }, - ], - }, - display: (value: unknown) => ({ - text: String(value), - color: - value === 'success' ? '#00FF00' : value === 'error' ? '#FF0000' : value === 'warning' ? '#FFFF00' : '#FF780A', - numeric: 0, - }), - }; - - const props = { - ...defaultProps, - field, - }; - it('CSV values', () => { + const mockField = fieldWithValues(['success,error,warning,unknown']); + const field = { + ...mockField, + config: { + ...mockField.config, + mappings: [ + { + type: MappingType.ValueToText, + options: { + success: { color: '#00FF00' }, + error: { color: '#FF0000' }, + warning: { color: '#FFFF00' }, + }, + }, + ], + }, + display: (value: unknown) => ({ + text: String(value), + color: + value === 'success' + ? '#00FF00' + : value === 'error' + ? '#FF0000' + : value === 'warning' + ? '#FFFF00' + : '#FF780A', + numeric: 0, + }), + } satisfies Field; + expectHTML( - render(), + render(), ` - success - error - warning - unknown + success + error + warning + unknown ` ); }); diff --git a/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.tsx b/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.tsx index c1ca23b7f69..35b759aa224 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/Cells/PillCell.tsx @@ -1,4 +1,3 @@ -import { css } from '@emotion/css'; import { useMemo } from 'react'; import { @@ -8,11 +7,36 @@ import { Field, getColorByStringHash, FALLBACK_COLOR, + fieldColorModeRegistry, } from '@grafana/data'; import { FieldColorModeId } from '@grafana/schema'; -import { useStyles2, useTheme2 } from '../../../../themes/ThemeContext'; -import { TableCellRendererProps } from '../types'; +import { PillCellProps, TableCellValue } from '../types'; + +export function PillCell({ rowIdx, field, theme }: PillCellProps) { + const value = field.values[rowIdx]; + const pills: Pill[] = useMemo(() => { + const pillValues = inferPills(value); + return pillValues.length > 0 ? createPills(pillValues, field, theme) : []; + }, [value, field, theme]); + + if (pills.length === 0) { + return null; + } + + return pills.map((pill) => ( + + {pill.value} + + )); +} interface Pill { value: string; @@ -21,6 +45,9 @@ interface Pill { color: string; } +const SPLIT_RE = /\s*,\s*/; +const TRANSPARENT = 'rgba(0,0,0,0)'; + function createPills(pillValues: string[], field: Field, theme: GrafanaTheme2): Pill[] { return pillValues.map((pill, index) => { const bgColor = getPillColor(pill, field, theme); @@ -34,38 +61,13 @@ function createPills(pillValues: string[], field: Field, theme: GrafanaTheme2): }); } -export function PillCell({ value, field }: TableCellRendererProps) { - const styles = useStyles2(getStyles); - const theme = useTheme2(); - - const pills: Pill[] = useMemo(() => { - const pillValues = inferPills(String(value)); - return createPills(pillValues, field, theme); - }, [value, field, theme]); - - return pills.map((pill) => ( - - {pill.value} - - )); -} - -const SPLIT_RE = /\s*,\s*/; -const TRANSPARENT = 'rgba(0,0,0,0)'; - -export function inferPills(value: string): string[] { - if (value === '') { +export function inferPills(rawValue: TableCellValue): string[] { + if (rawValue === '' || rawValue == null) { return []; } + const value = String(rawValue); + if (value[0] === '[') { try { return JSON.parse(value); @@ -77,6 +79,7 @@ export function inferPills(value: string): string[] { return value.trim().split(SPLIT_RE); } +// FIXME: this does not yet support "shades of a color" function getPillColor(value: string, field: Field, theme: GrafanaTheme2): string { const cfg = field.config; @@ -88,19 +91,14 @@ function getPillColor(value: string, field: Field, theme: GrafanaTheme2): string return theme.visualization.getColorByName(cfg.color?.fixedColor ?? FALLBACK_COLOR); } - // TODO: instead of classicColors we need to pull colors from theme, same way as FieldColorModeId.PaletteClassicByName (see fieldColor.ts) - return getColorByStringHash(classicColors, value); -} + let colors = classicColors; + const configuredColor = cfg.color; + if (configuredColor) { + const mode = fieldColorModeRegistry.get(configuredColor.mode); + if (typeof mode?.getColors === 'function') { + colors = mode.getColors(theme); + } + } -export const getStyles = (theme: GrafanaTheme2) => ({ - pill: css({ - display: 'inline-block', - padding: theme.spacing(0.25, 0.75), - marginInlineEnd: theme.spacing(0.5), - marginBlock: theme.spacing(0.5), - borderRadius: theme.shape.radius.default, - fontSize: theme.typography.bodySmall.fontSize, - lineHeight: theme.typography.bodySmall.lineHeight, - whiteSpace: 'nowrap', - }), -}); + return getColorByStringHash(colors, value); +} diff --git a/packages/grafana-ui/src/components/Table/TableNG/Cells/RowExpander.tsx b/packages/grafana-ui/src/components/Table/TableNG/Cells/RowExpander.tsx index 019afb7e243..d1f64824ef3 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/Cells/RowExpander.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/Cells/RowExpander.tsx @@ -7,8 +7,8 @@ import { useStyles2 } from '../../../../themes/ThemeContext'; import { Icon } from '../../../Icon/Icon'; import { RowExpanderNGProps } from '../types'; -export function RowExpander({ height, onCellExpand, isExpanded }: RowExpanderNGProps) { - const styles = useStyles2(getStyles, height); +export function RowExpander({ onCellExpand, isExpanded }: RowExpanderNGProps) { + const styles = useStyles2(getStyles); function handleKeyDown(e: React.KeyboardEvent) { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); @@ -30,12 +30,11 @@ export function RowExpander({ height, onCellExpand, isExpanded }: RowExpanderNGP ); } -const getStyles = (theme: GrafanaTheme2, rowHeight: number) => ({ +const getStyles = (_theme: GrafanaTheme2) => ({ expanderCell: css({ display: 'flex', flexDirection: 'column', justifyContent: 'center', - height: `${rowHeight}px`, cursor: 'pointer', }), }); diff --git a/packages/grafana-ui/src/components/Table/TableNG/Cells/TableCellActions.tsx b/packages/grafana-ui/src/components/Table/TableNG/Cells/TableCellActions.tsx index c66ed4be81c..746ca7027d1 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/Cells/TableCellActions.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/Cells/TableCellActions.tsx @@ -58,7 +58,7 @@ export function TableCellActions(props: TableCellActionsProps) { {showFilters && ( <> { onCellFilterAdded?.({ @@ -69,7 +69,7 @@ export function TableCellActions(props: TableCellActionsProps) { }} /> { onCellFilterAdded?.({ diff --git a/packages/grafana-ui/src/components/Table/TableNG/Cells/renderers.tsx b/packages/grafana-ui/src/components/Table/TableNG/Cells/renderers.tsx index 8d444eddf40..c406d7ffc3f 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/Cells/renderers.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/Cells/renderers.tsx @@ -11,6 +11,7 @@ import { BarGaugeCell } from './BarGaugeCell'; import { DataLinksCell } from './DataLinksCell'; import { GeoCell } from './GeoCell'; import { ImageCell } from './ImageCell'; +import { MarkdownCell } from './MarkdownCell'; import { PillCell } from './PillCell'; import { SparklineCell } from './SparklineCell'; @@ -78,6 +79,10 @@ const CUSTOM_RENDERER: TableCellRenderer = (props) => { return ; }; +const MARKDOWN_RENDERER: TableCellRenderer = (props) => ( + +); + const CELL_RENDERERS: Record = { [TableCellDisplayMode.Sparkline]: SPARKLINE_RENDERER, [TableCellDisplayMode.Gauge]: GAUGE_RENDERER, @@ -89,9 +94,16 @@ const CELL_RENDERERS: Record = { [TableCellDisplayMode.ColorText]: AUTO_RENDERER, [TableCellDisplayMode.ColorBackground]: AUTO_RENDERER, [TableCellDisplayMode.Auto]: AUTO_RENDERER, + [TableCellDisplayMode.Markdown]: MARKDOWN_RENDERER, [TableCellDisplayMode.Pill]: PILL_RENDERER, }; +// TODO: come up with a more elegant way to handle this. +const STRING_ONLY_RENDERERS = new Set([ + TableCellDisplayMode.Markdown, + TableCellDisplayMode.Pill, +]); + /** @internal */ export function getCellRenderer(field: Field, cellOptions: TableCellOptions): TableCellRenderer { const cellType = cellOptions?.type ?? TableCellDisplayMode.Auto; @@ -99,8 +111,7 @@ export function getCellRenderer(field: Field, cellOptions: TableCellOptions): Ta return getAutoRendererResult(field); } - // TODO: add support boolean, enum, (maybe int). but for now just string fields - if (cellType === TableCellDisplayMode.Pill && field.type !== FieldType.string) { + if (STRING_ONLY_RENDERERS.has(cellType) && field.type !== FieldType.string) { return AUTO_RENDERER; } diff --git a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx index 40cd49278d3..82ca9192066 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx @@ -84,6 +84,7 @@ export function TableNG(props: TableNGProps) { const { cellHeight, data, + disableSanitizeHtml, enablePagination = false, enableSharedCrosshair = false, enableVirtualization, @@ -160,12 +161,15 @@ export function TableNG(props: TableNGProps) { setSortColumns, } = useSortedRows(filteredRows, data.fields, { hasNestedFrames, initialSortBy }); - const defaultRowHeight = getDefaultRowHeight(theme, cellHeight); const [isInspecting, setIsInspecting] = useState(false); const [expandedRows, setExpandedRows] = useState(() => new Set()); // vt scrollbar accounting for column auto-sizing const visibleFields = useMemo(() => getVisibleFields(data.fields), [data.fields]); + const defaultRowHeight = useMemo( + () => getDefaultRowHeight(theme, visibleFields, cellHeight), + [theme, visibleFields, cellHeight] + ); const gridRef = useRef(null); const scrollbarWidth = useScrollbarWidth(gridRef, height, sortedRows); const availableWidth = useMemo( @@ -212,7 +216,7 @@ export function TableNG(props: TableNGProps) { width: availableWidth, height, headerHeight, - footerHeight: hasFooter ? defaultRowHeight : 0, + footerHeight: hasFooter ? (typeof defaultRowHeight === 'number' ? defaultRowHeight : TABLE.MAX_CELL_HEIGHT) : 0, rowHeight, }); @@ -224,6 +228,17 @@ export function TableNG(props: TableNGProps) { }); const applyToRowBgFn = useMemo(() => getApplyToRowBgFn(data.fields, theme) ?? undefined, [data.fields, theme]); + // normalize the row height into a function which returns a number, so we avoid a bunch of conditionals during rendering. + const rowHeightFn = useMemo((): ((row: TableRow) => number) => { + if (typeof rowHeight === 'function') { + return rowHeight; + } + if (typeof rowHeight === 'string') { + return () => TABLE.MAX_CELL_HEIGHT; + } + return () => rowHeight; + }, [rowHeight]); + const renderRow = useMemo( () => renderRowFactory(data.fields, panelContext, expandedRows, enableSharedCrosshair), [data, enableSharedCrosshair, expandedRows, panelContext] @@ -232,7 +247,7 @@ export function TableNG(props: TableNGProps) { const commonDataGridProps = useMemo( () => ({ - enableVirtualization, + enableVirtualization: enableVirtualization !== false && rowHeight !== 'auto', defaultColumnOptions: { minWidth: 50, resizable: true, @@ -348,8 +363,8 @@ export function TableNG(props: TableNGProps) { ) : undefined; - const shouldOverflow = shouldTextOverflow(field); - const shouldWrap = shouldTextWrap(field); + const shouldOverflow = rowHeight !== 'auto' && shouldTextOverflow(field); + const shouldWrap = rowHeight === 'auto' || shouldTextWrap(field); const withTooltip = withDataLinksActionsTooltip(field, cellType); const canBeColorized = cellType === TableCellDisplayMode.ColorBackground || cellType === TableCellDisplayMode.ColorText; @@ -367,14 +382,16 @@ export function TableNG(props: TableNGProps) { case TableCellDisplayMode.ColorText: case TableCellDisplayMode.DataLinks: case TableCellDisplayMode.JSONView: + case TableCellDisplayMode.Pill: + case TableCellDisplayMode.Markdown: cellClass = getCellStyles( theme, + cellType, textAlign, shouldWrap, shouldOverflow, canBeColorized, - isMonospace, - cellType === TableCellDisplayMode.DataLinks + isMonospace ); break; } @@ -427,7 +444,9 @@ export function TableNG(props: TableNGProps) { const value = props.row[props.column.key]; // TODO: it would be nice to get rid of passing height down as a prop. but this value // is cached so the cost of calling for every cell is low. - const height = typeof rowHeight === 'function' ? rowHeight(props.row) : rowHeight; + // NOTE: some cell types still require a height to be passed down, so that's why string-based + // cell types are going to just pass down the max cell height as a numeric height for those cells. + const height = rowHeightFn(props.row); const frame = data; return ( @@ -445,6 +464,7 @@ export function TableNG(props: TableNGProps) { cellInspect, showFilters, getActions: getCellActions, + disableSanitizeHtml, })} {showActions && ( { if (expandedRows.has(rowIdx)) { @@ -607,7 +626,7 @@ export function TableNG(props: TableNGProps) { crossFilterOrder, crossFilterRows, data, - defaultRowHeight, + disableSanitizeHtml, enableSharedCrosshair, expandedRows, filter, @@ -617,6 +636,7 @@ export function TableNG(props: TableNGProps) { onCellFilterAdded, panelContext, rowHeight, + rowHeightFn, rows, setFilter, showTypeIcons, @@ -821,6 +841,7 @@ const getGridStyles = ( border: 'none', '.rdg-cell': { + padding: TABLE.CELL_PADDING, '&:last-child': { borderInlineEnd: 'none', }, @@ -843,8 +864,6 @@ const getGridStyles = ( '.rdg-header-row, .rdg-summary-row': { '.rdg-cell': { zIndex: theme.zIndex.tooltip - 1, - paddingInline: TABLE.CELL_PADDING, - paddingBlock: TABLE.CELL_PADDING, }, }, }), @@ -941,35 +960,46 @@ const getHeaderCellStyles = (theme: GrafanaTheme2, justifyContent: Property.Just const getCellStyles = ( theme: GrafanaTheme2, + cellType: TableCellDisplayMode, textAlign: TextAlign, shouldWrap: boolean, shouldOverflow: boolean, isColorized: boolean, - isMonospace: boolean, - // TODO: replace this with cellTypeStyles: TemplateStringsArray object - isLinkCell: boolean -) => - css({ + isMonospace: boolean +) => { + const whiteSpace: CSSProperties['whiteSpace'] = (() => { + if (isMonospace) { + return 'pre'; + } + if (cellType === TableCellDisplayMode.Markdown) { + return 'normal'; + } + return 'pre-line'; + })(); + + return css({ display: 'flex', alignItems: 'center', textAlign, justifyContent: getJustifyContent(textAlign), - paddingInline: TABLE.CELL_PADDING, - minHeight: '100%', - backgroundClip: 'padding-box !important', // helps when cells have a bg color - ...(shouldWrap && { whiteSpace: isMonospace ? 'pre' : 'pre-line' }), + + ...(isColorized && { backgroundClip: 'padding-box !important' }), + ...(shouldOverflow && { minHeight: '100%' }), + ...(shouldWrap && { whiteSpace }), ...(isMonospace && { fontFamily: 'monospace' }), - // should omit if no cell actions, and no shouldOverflow '&:hover, &[aria-selected=true]': { '.table-cell-actions': { display: 'flex', }, ...(shouldOverflow && { - whiteSpace: 'pre-line', + zIndex: theme.zIndex.tooltip - 2, + whiteSpace, height: 'fit-content', minWidth: 'fit-content', - ...(isMonospace && { whiteSpace: 'pre' }), + ...(cellType === TableCellDisplayMode.Pill && { + flexWrap: 'wrap', + }), }), }, @@ -989,21 +1019,57 @@ const getCellStyles = ( }), }, - ...(isLinkCell && { + ...(cellType === TableCellDisplayMode.DataLinks && { + ...(shouldWrap && { + flexDirection: 'column', + justifyContent: 'center', + alignItems: getJustifyContent(textAlign), + }), '> a': { - // display: 'inline', // textWrap ? 'block' : 'inline', + flexWrap: 'nowrap', + ...(!shouldWrap && { + paddingInline: theme.spacing(0.5), + borderRight: `2px solid ${theme.colors.border.medium}`, + '&:first-child': { + paddingInlineStart: 0, + }, + '&:last-child': { + paddingInlineEnd: 0, + borderRight: 'none', + }, + }), + }, + }), + + ...(cellType === TableCellDisplayMode.Pill && { + display: 'inline-flex', + gap: theme.spacing(0.5), + flexWrap: shouldWrap ? 'wrap' : 'nowrap', + '> span': { + display: 'flex', + padding: theme.spacing(0.25, 0.75), + borderRadius: theme.shape.radius.default, + fontSize: theme.typography.bodySmall.fontSize, + lineHeight: theme.typography.bodySmall.lineHeight, whiteSpace: 'nowrap', - paddingInline: theme.spacing(1), - borderRight: `2px solid ${theme.colors.border.medium}`, + }, + }), - '&:first-of-type': { - paddingInlineStart: 0, - }, - - '&:last-of-type': { - borderRight: 'none', - paddingInlineEnd: 0, - }, + ...(cellType === TableCellDisplayMode.Markdown && { + '& ol, & ul': { + paddingLeft: theme.spacing(1.5), + }, + '& p': { + whiteSpace: 'pre-line', + }, + '& a': { + color: theme.colors.primary.text, + }, + // for elements like `p`, `h*`, etc. which have an inherent margin, + // we want to remove the bottom margin for the last one in the container. + '& > .markdown-container > *:last-child': { + marginBottom: 0, }, }), }); +}; diff --git a/packages/grafana-ui/src/components/Table/TableNG/hooks.test.ts b/packages/grafana-ui/src/components/Table/TableNG/hooks.test.ts index f7a1a1b858a..ce378961589 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/hooks.test.ts +++ b/packages/grafana-ui/src/components/Table/TableNG/hooks.test.ts @@ -490,24 +490,26 @@ describe('TableNG hooks', () => { const { fields } = setupData(); + let modifiedFields = fields.map((field) => { + if (field.name === 'name') { + return { + ...field, + name: 'Longer name that needs wrapping', + config: { + ...field.config, + custom: { + ...field.config?.custom, + wrapHeaderText: true, + }, + }, + }; + } + return field; + }); + renderHook(() => { return useHeaderHeight({ - fields: fields.map((field) => { - if (field.name === 'name') { - return { - ...field, - name: 'Longer name that needs wrapping', - config: { - ...field.config, - custom: { - ...field.config?.custom, - wrapHeaderText: true, - }, - }, - }; - } - return field; - }), + fields: modifiedFields, columnWidths: [100, 100, 100], enabled: true, typographyCtx: { ...typographyCtx, wrappedCount: countFn }, @@ -516,27 +518,29 @@ describe('TableNG hooks', () => { }); }); - expect(countFn).toHaveBeenCalledWith('Longer name that needs wrapping', 86); + expect(countFn).toHaveBeenCalledWith('Longer name that needs wrapping', 86, modifiedFields[0], -1); + + modifiedFields = fields.map((field) => { + if (field.name === 'name') { + return { + ...field, + name: 'Longer name that needs wrapping', + config: { + ...field.config, + custom: { + ...field.config?.custom, + filterable: true, + wrapHeaderText: true, + }, + }, + }; + } + return field; + }); renderHook(() => { return useHeaderHeight({ - fields: fields.map((field) => { - if (field.name === 'name') { - return { - ...field, - name: 'Longer name that needs wrapping', - config: { - ...field.config, - custom: { - ...field.config?.custom, - filterable: true, - wrapHeaderText: true, - }, - }, - }; - } - return field; - }), + fields: modifiedFields, columnWidths: [100, 100, 100], enabled: true, typographyCtx: { ...typographyCtx, wrappedCount: countFn }, @@ -545,7 +549,7 @@ describe('TableNG hooks', () => { }); }); - expect(countFn).toHaveBeenCalledWith('Longer name that needs wrapping', 26); + expect(countFn).toHaveBeenCalledWith('Longer name that needs wrapping', 26, modifiedFields[0], -1); }); }); @@ -764,7 +768,12 @@ describe('TableNG hooks', () => { expect(result.current(rows[0])).toEqual(expect.any(Number)); - expect(estimateLinesFn).toHaveBeenCalledWith('Annie Lennox', 100 - TABLE.CELL_PADDING * 2 - TABLE.BORDER_RIGHT); + expect(estimateLinesFn).toHaveBeenCalledWith( + 'Annie Lennox', + 100 - TABLE.CELL_PADDING * 2 - TABLE.BORDER_RIGHT, + fieldsWithWrappedText[0], + 0 + ); }); }); }); diff --git a/packages/grafana-ui/src/components/Table/TableNG/hooks.ts b/packages/grafana-ui/src/components/Table/TableNG/hooks.ts index 70a790d9133..47efa394409 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/hooks.ts +++ b/packages/grafana-ui/src/components/Table/TableNG/hooks.ts @@ -1,9 +1,9 @@ -import { useState, useMemo, useEffect, useCallback, useRef, useLayoutEffect, RefObject } from 'react'; +import { useState, useMemo, useCallback, useRef, useLayoutEffect, RefObject, CSSProperties } from 'react'; import { Column, DataGridHandle, DataGridProps, SortColumn } from 'react-data-grid'; import { Field, fieldReducers, FieldType, formattedValueToString, reduceField } from '@grafana/data'; -import { TableColumnResizeActionCallback } from '../types'; +import { TableCellDisplayMode, TableColumnResizeActionCallback } from '../types'; import { TABLE } from './constants'; import { FilterType, TableFooterCalc, TableRow, TableSortByFieldState, TableSummaryRow, TypographyCtx } from './types'; @@ -15,6 +15,7 @@ import { getRowHeight, buildHeaderLineCounters, buildRowLineCounters, + getCellOptions, } from './utils'; // Helper function to get displayed value @@ -134,7 +135,7 @@ export function useSortedRows( export interface PaginatedRowsOptions { height: number; width: number; - rowHeight: number | ((row: TableRow) => number); + rowHeight: NonNullable | ((row: TableRow) => number); headerHeight: number; footerHeight: number; paginationHeight?: number; @@ -173,6 +174,12 @@ export function usePaginatedRows( return rowHeight; } + // when using auto-sized rows, we're just going to have to pick a number. the alternative + // is to measure each row, which we could do but would be expensive. + if (typeof rowHeight === 'string') { + return TABLE.MAX_CELL_HEIGHT; + } + // we'll just measure 100 rows to estimate return rows.slice(0, 100).reduce((avg, row, _, { length }) => avg + rowHeight(row) / length, 0); }, [rows, rowHeight, enabled]); @@ -213,7 +220,7 @@ export function usePaginatedRows( }, [width, height, headerHeight, footerHeight, avgRowHeight, enabled, numRows, page]); // safeguard against page overflow on panel resize or other factors - useEffect(() => { + useLayoutEffect(() => { if (!enabled) { return; } @@ -380,7 +387,7 @@ interface UseRowHeightOptions { columnWidths: number[]; fields: Field[]; hasNestedFrames: boolean; - defaultHeight: number; + defaultHeight: NonNullable; expandedRows: Set; typographyCtx: TypographyCtx; } @@ -392,7 +399,7 @@ export function useRowHeight({ defaultHeight, expandedRows, typographyCtx, -}: UseRowHeightOptions): number | ((row: TableRow) => number) { +}: UseRowHeightOptions): NonNullable | ((row: TableRow) => number) { const lineCounters = useMemo(() => buildRowLineCounters(fields, typographyCtx), [fields, typographyCtx]); const hasWrappedCols = useMemo(() => lineCounters?.length ?? 0 > 0, [lineCounters]); @@ -403,7 +410,7 @@ export function useRowHeight({ const rowHeight = useMemo(() => { // row height is only complicated when there are nested frames or wrapped columns. - if (!hasNestedFrames && !hasWrappedCols) { + if ((!hasNestedFrames && !hasWrappedCols) || typeof defaultHeight === 'string') { return defaultHeight; } @@ -437,7 +444,15 @@ export function useRowHeight({ defaultHeight, lineCounters, TABLE.LINE_HEIGHT, - TABLE.CELL_PADDING * 2 + (field, numLines) => { + // Pill cells have vertical padding between each row + if (getCellOptions(field).type === TableCellDisplayMode.Pill) { + return TABLE.CELL_PADDING * (numLines - 1) + TABLE.CELL_PADDING * 2; + } + + // default vertical padding for cells + return TABLE.CELL_PADDING * 2; + } ); } return result; diff --git a/packages/grafana-ui/src/components/Table/TableNG/types.ts b/packages/grafana-ui/src/components/Table/TableNG/types.ts index 1d736974189..eaca19734ea 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/types.ts +++ b/packages/grafana-ui/src/components/Table/TableNG/types.ts @@ -141,6 +141,8 @@ export interface BaseTableProps { getActions?: GetActionsFunction; // Used solely for testing as RTL can't correctly render the table otherwise enableVirtualization?: boolean; + // for MarkdownCell, this flag disables sanitization of HTML content. Configured via config.ini. + disableSanitizeHtml?: boolean; } /* ---------------------------- Table cell props ---------------------------- */ @@ -161,6 +163,7 @@ export interface TableCellRendererProps { showFilters: boolean; justifyContent: Property.JustifyContent; getActions?: GetActionsFunctionLocal; + disableSanitizeHtml?: boolean; } export type ContextMenuProps = { @@ -186,7 +189,6 @@ export interface TableCellActionsProps { /* ------------------------- Specialized Cell Props ------------------------- */ export interface RowExpanderNGProps { - height: number; onCellExpand: (e: SyntheticEvent) => void; isExpanded?: boolean; } @@ -242,12 +244,24 @@ export interface AutoCellProps { rowIdx: number; } +export interface MarkdownCellProps { + field: Field; + rowIdx: number; + disableSanitizeHtml?: boolean; +} + export interface ActionCellProps { field: Field; rowIdx: number; getActions: GetActionsFunctionLocal; } +export interface PillCellProps { + theme: GrafanaTheme2; + field: Field; + rowIdx: number; +} + // Comparator for sorting table values export type Comparator = (a: TableCellValue, b: TableCellValue) => number; @@ -264,13 +278,14 @@ export interface ScrollPosition { export interface TypographyCtx { ctx: CanvasRenderingContext2D; - font: string; + fontFamily: string; + letterSpacing: number; avgCharWidth: number; estimateLines: LineCounter; wrappedCount: LineCounter; } -export type LineCounter = (value: unknown, width: number) => number; +export type LineCounter = (value: unknown, width: number, field: Field, rowIdx: number) => number; export interface LineCounterEntry { /** * given a values and the available width, returns the line count for that value diff --git a/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts b/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts index e86de2a7009..e159c9933cb 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts +++ b/packages/grafana-ui/src/components/Table/TableNG/utils.test.ts @@ -5,6 +5,7 @@ import { createTheme, DataFrame, DataFrameWithValue, + DataLink, DisplayValue, Field, FieldType, @@ -26,7 +27,6 @@ import { getCellLinks, getCellOptions, getComparator, - getDefaultRowHeight, getIsNestedTable, getAlignment, getJustifyContent, @@ -40,23 +40,18 @@ import { createTypographyContext, applySort, SINGLE_LINE_ESTIMATE_THRESHOLD, + wrapUwrapCount, + getDataLinksCounter, + getPillLineCounter, + getDefaultRowHeight, } from './utils'; describe('TableNG utils', () => { describe('alignment', () => { it.each(['left', 'center', 'right'] as const)('should return "%s" when configured', (align) => { - expect( - getAlignment({ - name: 'Value', - type: FieldType.string, - values: [], - config: { - custom: { - align, - }, - }, - }) - ).toBe(align); + expect(getAlignment({ name: 'Value', type: FieldType.string, values: [], config: { custom: { align } } })).toBe( + align + ); }); it.each([ @@ -65,16 +60,7 @@ describe('TableNG utils', () => { { type: FieldType.boolean, align: 'left' }, { type: FieldType.time, align: 'left' }, ])('should return "$align" for field type $type by default', ({ type, align }) => { - expect( - getAlignment({ - name: 'Test', - type, - values: [], - config: { - custom: {}, - }, - }) - ).toBe(align); + expect(getAlignment({ name: 'Test', type, values: [], config: { custom: {} } })).toBe(align); }); it.each([ @@ -91,17 +77,7 @@ describe('TableNG utils', () => { name: 'Test', type: FieldType.number, values: [], - config: { - custom: { - ...(cellType !== undefined - ? { - cellOptions: { - type: cellType, - }, - } - : {}), - }, - }, + config: { custom: { ...(cellType !== undefined ? { cellOptions: { type: cellType } } : {}) } }, }) ).toBe(align); }); @@ -122,34 +98,17 @@ describe('TableNG utils', () => { colors: { isDark: true, mode: 'dark', - primary: { - text: '#FFFFFF', - main: '#FF0000', - }, - background: { - canvas: '#000000', - primary: '#111111', - }, - text: { - primary: '#FFFFFF', - }, - action: { - hover: '#FF0000', - }, + primary: { text: '#FFFFFF', main: '#FF0000' }, + background: { canvas: '#000000', primary: '#111111' }, + text: { primary: '#FFFFFF' }, + action: { hover: '#FF0000' }, }, } as unknown as GrafanaTheme2; it('should handle color background mode', () => { - const field = { - type: TableCellDisplayMode.ColorBackground as const, - mode: TableCellBackgroundDisplayMode.Basic, - }; + const field = { type: TableCellDisplayMode.ColorBackground as const, mode: TableCellBackgroundDisplayMode.Basic }; - const displayValue = { - text: '100', - numeric: 100, - color: '#ff0000', - }; + const displayValue = { text: '100', numeric: 100, color: '#ff0000' }; const colors = getCellColors(theme, field, displayValue); expect(colors.bgColor).toBe('rgb(255, 0, 0)'); @@ -162,11 +121,7 @@ describe('TableNG utils', () => { mode: TableCellBackgroundDisplayMode.Gradient, }; - const displayValue = { - text: '100', - numeric: 100, - color: '#ff0000', - }; + const displayValue = { text: '100', numeric: 100, color: '#ff0000' }; const colors = getCellColors(theme, field, displayValue); expect(colors.bgColor).toBe('linear-gradient(120deg, rgb(255, 54, 36), #ff0000)'); @@ -185,12 +140,7 @@ describe('TableNG utils', () => { const records = frameToRecords(frame); expect(records).toHaveLength(2); - expect(records[0]).toEqual({ - __depth: 0, - __index: 0, - time: 1, - value: 10, - }); + expect(records[0]).toEqual({ __depth: 0, __index: 0, time: 1, value: 10 }); }); }); @@ -203,36 +153,22 @@ describe('TableNG utils', () => { config: {}, values: [1, 22, 333, 4444], // No state property initially - display: (value: unknown) => ({ - text: String(value), - numeric: Number(value), - }), + display: (value: unknown) => ({ text: String(value), numeric: Number(value) }), }; // Create a display value - const displayValue: DisplayValue = { - text: '1', - numeric: 1, - }; + const displayValue: DisplayValue = { text: '1', numeric: 1 }; // Call getAlignmentFactor with the first row const result = getAlignmentFactor(field, displayValue, 0); // Verify the result has the text property - expect(result).toEqual( - expect.objectContaining({ - text: '1', - }) - ); + expect(result).toEqual(expect.objectContaining({ text: '1' })); // Verify that field.state was created and contains the alignment factor expect(field.state).toBeDefined(); expect(field.state?.alignmentFactors).toBeDefined(); - expect(field.state?.alignmentFactors).toEqual( - expect.objectContaining({ - text: '1', - }) - ); + expect(field.state?.alignmentFactors).toEqual(expect.objectContaining({ text: '1' })); }); it('should update alignment factor when a longer value is found', () => { @@ -242,39 +178,21 @@ describe('TableNG utils', () => { type: FieldType.number, config: {}, values: [1, 22, 333, 4444], - state: { - alignmentFactors: { - text: '1', - }, - }, - display: (value: unknown) => ({ - text: String(value), - numeric: Number(value), - }), + state: { alignmentFactors: { text: '1' } }, + display: (value: unknown) => ({ text: String(value), numeric: Number(value) }), }; // Create a display value that is longer than the existing alignment factor - const displayValue: DisplayValue = { - text: '4444', - numeric: 4444, - }; + const displayValue: DisplayValue = { text: '4444', numeric: 4444 }; // Call getAlignmentFactor const result = getAlignmentFactor(field, displayValue, 3); // Verify the result is updated to the longer value - expect(result).toEqual( - expect.objectContaining({ - text: '4444', - }) - ); + expect(result).toEqual(expect.objectContaining({ text: '4444' })); // Verify that field.state.alignmentFactors was updated - expect(field.state?.alignmentFactors).toEqual( - expect.objectContaining({ - text: '4444', - }) - ); + expect(field.state?.alignmentFactors).toEqual(expect.objectContaining({ text: '4444' })); }); it('should not update alignment factor when a shorter value is found', () => { @@ -284,39 +202,21 @@ describe('TableNG utils', () => { type: FieldType.number, config: {}, values: [1, 22, 333, 4444], - state: { - alignmentFactors: { - text: '4444', - }, - }, - display: (value: unknown) => ({ - text: String(value), - numeric: Number(value), - }), + state: { alignmentFactors: { text: '4444' } }, + display: (value: unknown) => ({ text: String(value), numeric: Number(value) }), }; // Create a display value that is shorter than the existing alignment factor - const displayValue: DisplayValue = { - text: '1', - numeric: 1, - }; + const displayValue: DisplayValue = { text: '1', numeric: 1 }; // Call getAlignmentFactor const result = getAlignmentFactor(field, displayValue, 0); // Verify the result is still the longer value - expect(result).toEqual( - expect.objectContaining({ - text: '4444', - }) - ); + expect(result).toEqual(expect.objectContaining({ text: '4444' })); // Verify that field.state.alignmentFactors was not changed - expect(field.state?.alignmentFactors).toEqual( - expect.objectContaining({ - text: '4444', - }) - ); + expect(field.state?.alignmentFactors).toEqual(expect.objectContaining({ text: '4444' })); }); it('should add alignment factor to existing field state', () => { @@ -334,38 +234,24 @@ describe('TableNG utils', () => { // Or if noValue is a valid property: // noValue: true }, - display: (value: unknown) => ({ - text: String(value), - numeric: Number(value), - }), + display: (value: unknown) => ({ text: String(value), numeric: Number(value) }), }; // Create a display value - const displayValue: DisplayValue = { - text: '1', - numeric: 1, - }; + const displayValue: DisplayValue = { text: '1', numeric: 1 }; // Call getAlignmentFactor with the first row const result = getAlignmentFactor(field, displayValue, 0); // Verify the result has the text property - expect(result).toEqual( - expect.objectContaining({ - text: '1', - }) - ); + expect(result).toEqual(expect.objectContaining({ text: '1' })); // Verify that field.state was preserved and alignment factor was added expect(field.state).toBeDefined(); // Check for the valid property we used expect(field.state?.calcs).toBeDefined(); expect(field.state?.alignmentFactors).toBeDefined(); - expect(field.state?.alignmentFactors).toEqual( - expect.objectContaining({ - text: '1', - }) - ); + expect(field.state?.alignmentFactors).toEqual(expect.objectContaining({ text: '1' })); }); it.todo('alignmentFactor.text = displayValue.text;'); @@ -398,11 +284,7 @@ describe('TableNG utils', () => { ]; const result = getColumnTypes(fields); - expect(result).toEqual({ - name: FieldType.string, - age: FieldType.number, - active: FieldType.boolean, - }); + expect(result).toEqual({ name: FieldType.string, age: FieldType.number, active: FieldType.boolean }); }); it('should recursively build column types when nested fields are present', () => { @@ -448,20 +330,13 @@ describe('TableNG utils', () => { const frame: DataFrame = { fields: [ { type: FieldType.string, name: 'stringCol', config: {}, values: [] }, - { - type: FieldType.nestedFrames, - name: 'nestedCol', - config: {}, - values: [], - }, + { type: FieldType.nestedFrames, name: 'nestedCol', config: {}, values: [] }, ], length: 0, name: 'test', }; - expect(getColumnTypes(frame.fields)).toEqual({ - stringCol: FieldType.string, - }); + expect(getColumnTypes(frame.fields)).toEqual({ stringCol: FieldType.string }); }); }); @@ -557,18 +432,12 @@ describe('TableNG utils', () => { describe('migrateTableDisplayModeToCellOptions', () => { it('should migrate basic to gauge mode', () => { const result = migrateTableDisplayModeToCellOptions(TableCellDisplayMode.BasicGauge); - expect(result).toEqual({ - type: TableCellDisplayMode.Gauge, - mode: BarGaugeDisplayMode.Basic, - }); + expect(result).toEqual({ type: TableCellDisplayMode.Gauge, mode: BarGaugeDisplayMode.Basic }); }); it('should migrate gradient-gauge to gauge mode with gradient', () => { const result = migrateTableDisplayModeToCellOptions(TableCellDisplayMode.GradientGauge); - expect(result).toEqual({ - type: TableCellDisplayMode.Gauge, - mode: BarGaugeDisplayMode.Gradient, - }); + expect(result).toEqual({ type: TableCellDisplayMode.Gauge, mode: BarGaugeDisplayMode.Gradient }); }); it('should migrate color-background to color background with gradient', () => { @@ -581,20 +450,13 @@ describe('TableNG utils', () => { it('should handle other display modes', () => { const result = migrateTableDisplayModeToCellOptions(TableCellDisplayMode.ColorText); - expect(result).toEqual({ - type: TableCellDisplayMode.ColorText, - }); + expect(result).toEqual({ type: TableCellDisplayMode.ColorText }); }); }); describe('getCellOptions', () => { it('should return default options when no custom config is provided', () => { - const field: Field = { - name: 'test', - type: FieldType.string, - config: {}, - values: [], - }; + const field: Field = { name: 'test', type: FieldType.string, config: {}, values: [] }; const options = getCellOptions(field); @@ -607,35 +469,21 @@ describe('TableNG utils', () => { name: 'test', type: FieldType.string, config: { - custom: { - cellOptions: { - type: TableCellDisplayMode.ColorText, - inspectEnabled: false, - wrapText: true, - }, - }, + custom: { cellOptions: { type: TableCellDisplayMode.ColorText, inspectEnabled: false, wrapText: true } }, }, values: [], }; const options = getCellOptions(field); - expect(options).toEqual({ - type: TableCellDisplayMode.ColorText, - inspectEnabled: false, - wrapText: true, - }); + expect(options).toEqual({ type: TableCellDisplayMode.ColorText, inspectEnabled: false, wrapText: true }); }); it('should handle legacy displayMode property', () => { const field: Field = { name: 'test', type: FieldType.string, - config: { - custom: { - displayMode: 'color-background', - }, - }, + config: { custom: { displayMode: 'color-background' } }, values: [], }; @@ -649,14 +497,7 @@ describe('TableNG utils', () => { const field: Field = { name: 'test', type: FieldType.string, - config: { - custom: { - displayMode: 'color-background', - cellOptions: { - type: TableCellDisplayMode.ColorText, - }, - }, - }, + config: { custom: { displayMode: 'color-background', cellOptions: { type: TableCellDisplayMode.ColorText } } }, values: [], }; @@ -689,13 +530,7 @@ describe('TableNG utils', () => { const field: Field = { name: 'test', type: FieldType.string, - config: { - custom: { - cellOptions: { - type: TableCellDisplayMode.JSONView, - }, - }, - }, + config: { custom: { cellOptions: { type: TableCellDisplayMode.JSONView } } }, values: [], }; @@ -707,12 +542,7 @@ describe('TableNG utils', () => { describe('getCellLinks', () => { it('should return undefined when field has no getLinks function', () => { - const field: Field = { - name: 'test', - type: FieldType.string, - config: {}, - values: ['value'], - }; + const field: Field = { name: 'test', type: FieldType.string, config: {}, values: ['value'] }; const links = getCellLinks(field, 0); expect(links).toEqual(undefined); @@ -957,23 +787,46 @@ describe('TableNG utils', () => { describe('getDefaultRowHeight', () => { const theme = createTheme(); - it('returns correct height for TableCellHeight.Sm', () => { - const result = getDefaultRowHeight(theme, TableCellHeight.Sm); - expect(result).toBe(36); + it.each([ + { input: TableCellHeight.Sm, expected: 36 }, + { input: TableCellHeight.Md, expected: 42 }, + { input: TableCellHeight.Lg, expected: TABLE.MAX_CELL_HEIGHT }, + ])('returns "$expected" for "$input"', ({ input, expected }) => { + const result = getDefaultRowHeight(theme, [], input); + expect(result).toBe(expected); }); - it('returns correct height for TableCellHeight.Md', () => { - const result = getDefaultRowHeight(theme, TableCellHeight.Md); - expect(result).toBe(42); - }); - - it('returns correct height for TableCellHeight.Lg', () => { - const result = getDefaultRowHeight(theme, TableCellHeight.Lg); - expect(result).toBe(TABLE.MAX_CELL_HEIGHT); + it('returns "auto" if a field is present with the dynamicHeight cellOption is false', () => { + expect( + getDefaultRowHeight( + theme, + [ + { + name: 'test1', + type: FieldType.string, + config: {}, + values: ['value1'], + }, + { + name: 'test2', + type: FieldType.string, + config: { custom: { cellOptions: { type: TableCellDisplayMode.Markdown, dynamicHeight: true } } }, + values: ['value1'], + }, + { + name: 'test3', + type: FieldType.number, + config: { custom: { cellOptions: { type: TableCellDisplayMode.JSONView } } }, + values: [3], + }, + ], + TableCellHeight.Sm + ) + ).toBe('auto'); }); it('calculates height based on theme when cellHeight is undefined', () => { - const result = getDefaultRowHeight(theme, undefined as unknown as TableCellHeight); + const result = getDefaultRowHeight(theme, []); // Calculate the expected result based on the theme values const expected = TABLE.CELL_PADDING * 2 + theme.typography.fontSize * theme.typography.body.lineHeight; @@ -987,36 +840,112 @@ describe('TableNG utils', () => { // actually executed the JS correctly. If you called `count` with a sensible value and width, // it wouldn't give you a very reasonable answer in Jest's DOM environment for some reason. it('creates the context using uwrap', () => { + const field: Field = { name: 'test', type: FieldType.string, config: {}, values: ['foo', 'bar', 'baz'] }; const ctx = createTypographyContext(14, 'sans-serif', 0.15); + expect(ctx).toEqual( expect.objectContaining({ - font: '14px sans-serif', ctx: expect.any(CanvasRenderingContext2D), + fontFamily: 'sans-serif', + letterSpacing: 0.15, wrappedCount: expect.any(Function), estimateLines: expect.any(Function), avgCharWidth: expect.any(Number), }) ); - expect(ctx.wrappedCount('the quick brown fox jumps over the lazy dog', 100)).toEqual(expect.any(Number)); - expect(ctx.estimateLines('the quick brown fox jumps over the lazy dog', 100)).toEqual(expect.any(Number)); + expect(ctx.wrappedCount('the quick brown fox jumps over the lazy dog', 100, field, 0)).toEqual( + expect.any(Number) + ); + expect(ctx.estimateLines('the quick brown fox jumps over the lazy dog', 100, field, 0)).toEqual( + expect.any(Number) + ); + }); + }); + + describe('wrapUwrapCount', () => { + const field: Field = { name: 'test', type: FieldType.string, config: {}, values: ['foo', 'bar', 'baz'] }; + + it('wraps the uwrap count function', () => { + const wrappedCount = wrapUwrapCount(jest.fn(() => 2)); + expect(wrappedCount('test string', 100, field, 0)).toBe(2); + }); + + it('returns 1 for null or undefined values', () => { + const wrappedCount = wrapUwrapCount(jest.fn(() => 2)); + expect(wrappedCount(null, 100, field, 0)).toBe(1); + expect(wrappedCount(undefined, 100, field, 0)).toBe(1); }); }); describe('getTextLineEstimator', () => { const counter = getTextLineEstimator(10); + const field: Field = { name: 'test', type: FieldType.string, config: {}, values: ['foo', 'bar', 'baz'] }; it('returns -1 if there are no strings or dashes within the string', () => { - expect(counter('asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf', 5)).toBe(-1); + expect(counter('asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf', 5, field, 0)).toBe(-1); }); it('calculates an approximate rendered height for the text based on the width and avgCharWidth', () => { - expect(counter('asdfas dfasdfasdf asdfasdfasdfa sdfasdfasdfasdf 23', 200)).toBe(2.5); + expect(counter('asdfas dfasdfasdf asdfasdfasdfa sdfasdfasdfasdf 23', 200, field, 0)).toBe(2.5); + }); + }); + + describe('getDataLinksCounter', () => { + it('counts number of valid links using getCellLinks', () => { + const field: Field = { + name: 'test', + type: FieldType.string, + config: { + links: [ + { title: 'Link 1', url: 'http://example.com/1' }, + { title: 'Invalid Link' } as DataLink, // No href or onClick + { + title: 'Link w', + url: 'asdf', + onClick: jest.fn(() => {}), + }, + ], + }, + values: ['value1'], + }; + + const counter = getDataLinksCounter(); + expect(counter('my value', 100, field, 0)).toBe(2); + }); + }); + + describe('getPillLineCounter', () => { + it('counts up the number of lines using the pill measuring method', () => { + const counter = getPillLineCounter(jest.fn((str) => str.length * 5)); + expect(counter('tag1,tag2', 100, {} as Field, 0)).toBe(1); + expect(counter('tag1,tag2,tag3,tag4,tag5,tag6', 100, {} as Field, 0)).toBe(3); + }); + + it('returns 0 if value is null', () => { + const counter = getPillLineCounter(jest.fn((str) => str.length * 5)); + expect(counter(null, 100, {} as Field, 0)).toBe(0); + }); + + it('returns 0 if no pills are inferred', () => { + const counter = getPillLineCounter(jest.fn((str) => str.length * 5)); + expect(counter('', 100, {} as Field, 0)).toBe(0); + }); + + it('caches the width measurement for the same value', () => { + const widthMeasurement = jest.fn((str) => str.length * 5); + const counter = getPillLineCounter(widthMeasurement); + counter('tag1,tag2,tag3,tag4,tag5,tag6', 100, {} as Field, 0); + counter('tag1,tag2', 100, {} as Field, 0); + counter('tag2', 200, {} as Field, 0); + counter('tag2,tag3,tag2,tag4,tag4,tag2,tag5', 300, {} as Field, 0); + expect(widthMeasurement).toHaveBeenCalledTimes(6); // Should only call for unique values }); }); describe('buildHeaderLineCounters', () => { const ctx = { - font: '14px sans-serif', + fontFamily: 'sans-serif', + letterSpacing: 0.15, ctx: {} as CanvasRenderingContext2D, count: jest.fn(() => 2), avgCharWidth: 7, @@ -1057,15 +986,15 @@ describe('TableNG utils', () => { describe('buildRowLineCounters', () => { const ctx = { - font: '14px sans-serif', + fontFamily: 'sans-serif', + letterSpacing: 0.15, ctx: {} as CanvasRenderingContext2D, - count: jest.fn(() => 2), wrappedCount: jest.fn(() => 2), estimateLines: jest.fn(() => 2), avgCharWidth: 7, }; - it('returns an array of line counters for each column', () => { + it('sets up text line counters for each text column if wrapping is on', () => { const fields: Field[] = [ { name: 'Name', type: FieldType.string, values: [], config: { custom: { cellOptions: { wrapText: true } } } }, { @@ -1095,6 +1024,42 @@ describe('TableNG utils', () => { expect(counters![0].fieldIdxs).toEqual([1]); }); + it('sets up line counting for pills if present and wrapping is on', () => { + const fields: Field[] = [ + { + name: 'Tags', + type: FieldType.string, + values: ['tag1,tag2', 'tag3', '["tag4","tag5","tag6"]'], + config: { custom: { cellOptions: { type: TableCellDisplayMode.Pill, wrapText: true } } }, + }, + ]; + const counters = buildRowLineCounters(fields, ctx); + expect(counters![0].estimate).toEqual(expect.any(Function)); + expect(counters![0].estimate!('tag1,tag2', 100, fields[0], 0)).toEqual(expect.any(Number)); + expect(counters![0].counter).toEqual(expect.any(Function)); + expect(counters![0].counter('tag1,tag2', 100, fields[0], 0)).toEqual(expect.any(Number)); + expect(counters![0].fieldIdxs).toEqual([0]); + }); + + it('sets up line counting for datalinks if present and wrapping is on', () => { + const fields: Field[] = [ + { + name: 'Links', + type: FieldType.string, + values: ['http://example.com/1', 'http://example.com/2'], + config: { custom: { cellOptions: { type: TableCellDisplayMode.DataLinks, wrapText: true } } }, + getLinks: jest.fn((): LinkModel[] => [ + { title: 'Link 1', href: 'http://example.com/1', target: '_blank', origin: { datasourceUid: 'test' } }, + { title: 'Link 2', href: 'http://example.com/2', target: '_self', origin: { datasourceUid: 'test' } }, + ]), + }, + ]; + const counters = buildRowLineCounters(fields, ctx); + expect(counters![0].counter).toEqual(expect.any(Function)); + expect(counters![0].counter('http://example.com/1', 100, fields[0], 0)).toEqual(expect.any(Number)); + expect(counters![0].fieldIdxs).toEqual([0]); + }); + it('does not enable text counting for non-string fields', () => { const fields: Field[] = [ { name: 'Name', type: FieldType.string, values: [], config: { custom: {} } }, @@ -1162,15 +1127,15 @@ describe('TableNG utils', () => { it('should take colWidths into account when calculating max wrap cell', () => { getRowHeight(fields, 3, [50, 60], 36, counters, 20, 10); - expect(counters[0].counter).toHaveBeenCalledWith('longer one here', 50); - expect(counters[1].counter).toHaveBeenCalledWith(123456, 60); + expect(counters[0].counter).toHaveBeenCalledWith('longer one here', 50, fields[0], 3); + expect(counters[1].counter).toHaveBeenCalledWith(123456, 60, fields[1], 3); }); // this is used to calc wrapped header height it('should use the display name if the rowIdx is -1', () => { getRowHeight(fields, -1, [50, 60], 36, counters, 20, 10); - expect(counters[0].counter).toHaveBeenCalledWith('Name', 50); - expect(counters[1].counter).toHaveBeenCalledWith('Age', 60); + expect(counters[0].counter).toHaveBeenCalledWith('Name', 50, fields[0], -1); + expect(counters[1].counter).toHaveBeenCalledWith('Age', 60, fields[1], -1); }); it('should ignore columns which do not have line counters', () => { @@ -1230,18 +1195,8 @@ describe('TableNG utils', () => { expect( computeColWidths( [ - { - name: 'A', - type: FieldType.string, - values: [], - config: { custom: { width: 100 } }, - }, - { - name: 'B', - type: FieldType.string, - values: [], - config: { custom: { width: 200 } }, - }, + { name: 'A', type: FieldType.string, values: [], config: { custom: { width: 100 } } }, + { name: 'B', type: FieldType.string, values: [], config: { custom: { width: 200 } } }, ], 500 ) @@ -1252,18 +1207,8 @@ describe('TableNG utils', () => { expect( computeColWidths( [ - { - name: 'A', - type: FieldType.string, - values: [], - config: {}, - }, - { - name: 'B', - type: FieldType.string, - values: [], - config: { custom: { width: 200 } }, - }, + { name: 'A', type: FieldType.string, values: [], config: {} }, + { name: 'B', type: FieldType.string, values: [], config: { custom: { width: 200 } } }, ], 500 ) @@ -1274,18 +1219,8 @@ describe('TableNG utils', () => { expect( computeColWidths( [ - { - name: 'A', - type: FieldType.string, - values: [], - config: { custom: { minWidth: 100 } }, - }, - { - name: 'B', - type: FieldType.string, - values: [], - config: { custom: { minWidth: 100 } }, - }, + { name: 'A', type: FieldType.string, values: [], config: { custom: { minWidth: 100 } } }, + { name: 'B', type: FieldType.string, values: [], config: { custom: { minWidth: 100 } } }, ], 100 ) @@ -1296,18 +1231,8 @@ describe('TableNG utils', () => { expect( computeColWidths( [ - { - name: 'A', - type: FieldType.string, - values: [], - config: {}, - }, - { - name: 'B', - type: FieldType.string, - values: [], - config: {}, - }, + { name: 'A', type: FieldType.string, values: [], config: {} }, + { name: 'B', type: FieldType.string, values: [], config: {} }, ], // we have two columns but have set the table to the width of one default column. COLUMN.DEFAULT_WIDTH @@ -1332,28 +1257,14 @@ describe('TableNG utils', () => { ], }); - const sortColumns: SortColumn[] = [ - { - columnKey: 'time', - direction: 'ASC', - }, - ]; + const sortColumns: SortColumn[] = [{ columnKey: 'time', direction: 'ASC' }]; const records = applySort(frameToRecords(frame), frame.fields, sortColumns); expect(records).toMatchObject([ - { - time: 1, - value: 20, - }, - { - time: 1, - value: 10, - }, - { - time: 2, - value: 30, - }, + { time: 1, value: 20 }, + { time: 1, value: 10 }, + { time: 2, value: 30 }, ]); }); }); diff --git a/packages/grafana-ui/src/components/Table/TableNG/utils.ts b/packages/grafana-ui/src/components/Table/TableNG/utils.ts index a24a361bfda..9ba4a5eddca 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/utils.ts +++ b/packages/grafana-ui/src/components/Table/TableNG/utils.ts @@ -1,4 +1,5 @@ import { Property } from 'csstype'; +import { CSSProperties } from 'react'; import { SortColumn } from 'react-data-grid'; import tinycolor from 'tinycolor2'; import { Count, varPreLine } from 'uwrap'; @@ -25,6 +26,7 @@ import { import { getTextColorForAlphaBackground } from '../../../utils/colors'; import { TableCellOptions } from '../types'; +import { inferPills } from './Cells/PillCell'; import { COLUMN, TABLE } from './constants'; import { CellColors, @@ -44,9 +46,14 @@ export type CellNumLinesCalculator = (text: string, cellWidth: number) => number * @internal * Returns the default row height based on the theme and cell height setting. */ -export function getDefaultRowHeight(theme: GrafanaTheme2, cellHeight?: TableCellHeight): number { - const bodyFontSize = theme.typography.fontSize; - const lineHeight = theme.typography.body.lineHeight; +export function getDefaultRowHeight( + theme: GrafanaTheme2, + fields?: Field[], + cellHeight?: TableCellHeight +): NonNullable { + if (fields?.some((field) => field.config?.custom?.cellOptions?.dynamicHeight)) { + return 'auto'; + } switch (cellHeight) { case TableCellHeight.Sm: @@ -57,7 +64,7 @@ export function getDefaultRowHeight(theme: GrafanaTheme2, cellHeight?: TableCell return TABLE.MAX_CELL_HEIGHT; } - return TABLE.CELL_PADDING * 2 + bodyFontSize * lineHeight; + return TABLE.CELL_PADDING * 2 + theme.typography.fontSize * theme.typography.body.lineHeight; } /** @@ -92,15 +99,19 @@ export function createTypographyContext(fontSize: number, fontFamily: string, le ctx.letterSpacing = `${letterSpacing}px`; ctx.font = font; + // 1/6 of the characters in this string are capitalized. Since the avgCharWidth is used for estimation, it's + // better that the estimation over-estimates the width than if it underestimates it, so we're a little on the + // aggressive side here and could even go more aggressive if we get complaints in the future. const txt = - "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s."; + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s. 1234567890 ALL CAPS TO HELP WITH MEASUREMENT."; const txtWidth = ctx.measureText(txt).width; const avgCharWidth = txtWidth / txt.length + letterSpacing; const { count } = varPreLine(ctx); return { ctx, - font, + fontFamily, + letterSpacing, avgCharWidth, estimateLines: getTextLineEstimator(avgCharWidth), wrappedCount: wrapUwrapCount(count), @@ -108,7 +119,7 @@ export function createTypographyContext(fontSize: number, fontFamily: string, le } /** - * @internal + * @internal wraps the uwrap count function to ensure that it is given a string. */ export function wrapUwrapCount(count: Count): LineCounter { return (value, width) => { @@ -141,6 +152,71 @@ export function getTextLineEstimator(avgCharWidth: number): LineCounter { }; } +/** + * @internal + */ +export function getDataLinksCounter(): LineCounter { + const linksCountCache: Record = {}; + + // when we render links, we need to filter out the invalid links. since the call to `getLinks` is expensive, + // we'll cache the result and reuse it for every row in the table. this cache is cleared when line counts are + // rebuilt anytime from the `useRowHeight` hook, and that includes adding and removing data links. + return (_value, _width, field) => { + const cacheKey = getDisplayName(field); + if (linksCountCache[cacheKey] === undefined) { + let count = 0; + for (const l of field.config?.links ?? []) { + if (l.onClick || l.url) { + count += 1; + } + } + linksCountCache[cacheKey] = count; + } + + return linksCountCache[cacheKey]; + }; +} + +const PILLS_FONT_SIZE = 12; +const PILLS_SPACING = 12; // 6px horizontal padding on each side +const PILLS_GAP = 4; // gap between pills + +export function getPillLineCounter(measureWidth: (value: string) => number): LineCounter { + const widthCache: Record = {}; + + return (value, width) => { + if (value == null) { + return 0; + } + + const pillValues = inferPills(String(value)); + if (pillValues.length === 0) { + return 0; + } + + let lines = 0; + let currentLineUse = width; + + for (const pillValue of pillValues) { + let rawWidth = widthCache[pillValue]; + if (rawWidth === undefined) { + rawWidth = measureWidth(pillValue); + widthCache[pillValue] = rawWidth; + } + const pillWidth = rawWidth + PILLS_SPACING; + + if (currentLineUse + pillWidth + PILLS_GAP > width) { + lines++; + currentLineUse = pillWidth; + } else { + currentLineUse += pillWidth + PILLS_GAP; + } + } + + return lines; + }; +} + /** * @internal return a text line counter for every field which has wrapHeaderText enabled. */ @@ -175,12 +251,33 @@ export function buildRowLineCounters(fields: Field[], typographyCtx: TypographyC const field = fields[fieldIdx]; if (shouldTextWrap(field)) { wrappedFields++; - // TODO: Pills, DataLinks, and JSON will have custom line counters here. - // for string fields, we really want to find the longest field ahead of time to reduce the number of calls to `count`. - // calling `count` is going to get a perfectly accurate line count, but it is expensive, so we'd rather estimate the line - // count and call the counter only for the field which will take up the most space based on its - if (field.type === FieldType.string) { + const cellType = getCellOptions(field).type; + if (cellType === TableCellDisplayMode.DataLinks) { + result.dataLinksCounter = result.dataLinksCounter ?? { + counter: getDataLinksCounter(), + fieldIdxs: [], + }; + result.dataLinksCounter.fieldIdxs.push(fieldIdx); + } else if (cellType === TableCellDisplayMode.Pill) { + if (!result.pillCounter) { + const pillTypographyCtx = createTypographyContext( + PILLS_FONT_SIZE, + typographyCtx.fontFamily, + typographyCtx.letterSpacing + ); + + result.pillCounter = { + estimate: getPillLineCounter((value) => value.length * pillTypographyCtx.avgCharWidth), + counter: getPillLineCounter((value) => pillTypographyCtx.ctx.measureText(value).width), + fieldIdxs: [], + }; + } + result.pillCounter.fieldIdxs.push(fieldIdx); + } + + // for string fields, we estimate the length of a line using `avgCharWidth` to limit expensive calls `count`. + else if (field.type === FieldType.string) { result.textCounter = result.textCounter ?? { counter: typographyCtx.wrappedCount, estimate: typographyCtx.estimateLines, @@ -215,7 +312,9 @@ export function getRowHeight( defaultHeight: number, lineCounters?: LineCounterEntry[], lineHeight = TABLE.LINE_HEIGHT, - verticalPadding = 0 + // when this is a function, the field which was measured as the maximum size will be returned, as well as the + // calculated number of lines, so that the consumer can use it in case the vertical padding value differs field-by-field. + verticalPadding: number | ((field: Field, numLines: number) => number) = TABLE.CELL_PADDING ): number { if (!lineCounters?.length) { return defaultHeight; @@ -224,6 +323,7 @@ export function getRowHeight( let maxLines = -1; let maxValue = ''; let maxWidth = 0; + let maxField: Field | undefined; let preciseCounter: LineCounter | undefined; for (const { estimate, counter, fieldIdxs } of lineCounters) { @@ -239,11 +339,12 @@ export function getRowHeight( const cellValueRaw = rowIdx === -1 ? getDisplayName(field) : field.values[rowIdx]; if (cellValueRaw != null) { const colWidth = columnWidths[fieldIdx]; - const approxLines = count(cellValueRaw, colWidth); + const approxLines = count(cellValueRaw, colWidth, field, rowIdx); if (approxLines > maxLines) { maxLines = approxLines; maxValue = cellValueRaw; maxWidth = colWidth; + maxField = field; preciseCounter = isEstimating ? counter : undefined; } } @@ -252,18 +353,23 @@ export function getRowHeight( // if the value is -1 or the estimate for the max cell was less than the SINGLE_LINE_ESTIMATE_THRESHOLD, we trust // that the estimator correctly identified that no text wrapping is needed for this row, skipping the preciseCounter. - if (maxLines < SINGLE_LINE_ESTIMATE_THRESHOLD) { + if (maxField === undefined || maxLines < SINGLE_LINE_ESTIMATE_THRESHOLD) { return defaultHeight; } // if we finished this row height loop with an estimate, we need to call // the `preciseCounter` method to get the exact line count. if (preciseCounter !== undefined) { - maxLines = preciseCounter(maxValue, maxWidth); + maxLines = preciseCounter(maxValue, maxWidth, maxField, rowIdx); } - // we want a round number of lines for rendering - const totalHeight = Math.ceil(maxLines) * lineHeight + verticalPadding; + // round up to the nearest line before doing math + maxLines = Math.ceil(maxLines); + + // adjust for vertical padding and line height, and clamp to a minimum default height + const verticalPaddingValue = + typeof verticalPadding === 'function' ? verticalPadding(maxField, maxLines) : verticalPadding; + const totalHeight = maxLines * lineHeight + verticalPaddingValue; return Math.max(totalHeight, defaultHeight); } @@ -276,9 +382,7 @@ export function shouldTextOverflow(field: Field): boolean { const eligibleCellType = // Tech debt: Technically image cells are of type string, which is misleading (kinda?) // so we need to ensurefield.type === FieldType.string we don't apply overflow hover states for type image - (field.type === FieldType.string && - cellOptions.type !== TableCellDisplayMode.Image && - cellOptions.type !== TableCellDisplayMode.Pill) || + (field.type === FieldType.string && cellOptions.type !== TableCellDisplayMode.Image) || // regardless of the underlying cell type, data links cells have text overflow. cellOptions.type === TableCellDisplayMode.DataLinks; diff --git a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list b/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list deleted file mode 100644 index 49a2170ce4e..00000000000 --- a/pkg/apis/provisioning/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ /dev/null @@ -1,26 +0,0 @@ -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,DeleteJobOptions,Paths -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,DeleteJobOptions,Resources -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,FileList,Items -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,HistoryList,Items -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobResourceSummary,Errors -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobStatus,Errors -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobStatus,Summary -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,ManagerStats,Stats -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,MoveJobOptions,Paths -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,MoveJobOptions,Resources -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RefList,Items -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositoryList,Items -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositorySpec,Workflows -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositoryView,Workflows -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositoryViewList,AvailableRepositoryTypes -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositoryViewList,Items -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,ResourceList,Items -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,TestResults,Errors -API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,WebhookStatus,SubscribedEvents -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobSpec,PullRequest -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,ManagerStats,Identity -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositorySpec,GitHub -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositorySpec,GitLab -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,ResourceWrapper,URLs -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,SyncStatus,JobID -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,WebhookResponse,Message diff --git a/pkg/expr/nodes.go b/pkg/expr/nodes.go index 83212715625..300488ae643 100644 --- a/pkg/expr/nodes.go +++ b/pkg/expr/nodes.go @@ -407,23 +407,11 @@ func (dn *DSNode) Execute(ctx context.Context, now time.Time, _ mathexp.Vars, s return mathexp.Results{}, MakeQueryError(dn.refID, dn.datasource.UID, err) } } else { - // transform request from backend.QueryDataRequest to k8s request - k8sReq := &data.QueryDataRequest{ - TimeRange: data.TimeRange{ - From: req.Queries[0].TimeRange.From.Format(time.RFC3339), - To: req.Queries[0].TimeRange.To.Format(time.RFC3339), - }, + k8sReq, err := ConvertBackendRequestToDataRequest(req) + if err != nil { + return mathexp.Results{}, MakeQueryError(dn.refID, dn.datasource.UID, err) } - for _, q := range req.Queries { - var dataQuery data.DataQuery - err := json.Unmarshal(q.JSON, &dataQuery) - if err != nil { - return mathexp.Results{}, MakeQueryError(dn.refID, dn.datasource.UID, err) - } - k8sReq.Queries = append(k8sReq.Queries, dataQuery) - } - var err error // make the query with a mt client resp, err = mtDSClient.QueryData(ctx, *k8sReq) diff --git a/pkg/expr/query_convert.go b/pkg/expr/query_convert.go new file mode 100644 index 00000000000..cb8f6b2e08f --- /dev/null +++ b/pkg/expr/query_convert.go @@ -0,0 +1,51 @@ +package expr + +import ( + "encoding/json" + "strconv" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" +) + +func convertBackendQueryToDataQuery(q backend.DataQuery) (data.DataQuery, error) { + // we first restore it from the raw json data, + // this should take care of all datasource-specific (for example, specific + // for prometheus or loki) fields + var dataQuery data.DataQuery + err := json.Unmarshal(q.JSON, &dataQuery) + if err != nil { + return data.DataQuery{}, err + } + + // then we override the result with values that are available as fields in backend.DataQuery + dataQuery.RefID = q.RefID + dataQuery.QueryType = q.QueryType + dataQuery.MaxDataPoints = q.MaxDataPoints + dataQuery.IntervalMS = float64(q.Interval.Nanoseconds()) / 1000000.0 + dataQuery.TimeRange = &data.TimeRange{ + From: strconv.FormatInt(q.TimeRange.From.UnixMilli(), 10), + To: strconv.FormatInt(q.TimeRange.To.UnixMilli(), 10), + } + + return dataQuery, nil +} + +func ConvertBackendRequestToDataRequest(req *backend.QueryDataRequest) (*data.QueryDataRequest, error) { + k8sReq := &data.QueryDataRequest{ + // `backend.QueryDataRequest` does not have a concept of a top-level global from/to. + // timeRanges are always inside the queries, + // so we leave them empty here too. + } + + for _, q := range req.Queries { + dataQuery, err := convertBackendQueryToDataQuery(q) + if err != nil { + return nil, err + } + + k8sReq.Queries = append(k8sReq.Queries, dataQuery) + } + + return k8sReq, nil +} diff --git a/pkg/expr/query_convert_test.go b/pkg/expr/query_convert_test.go new file mode 100644 index 00000000000..1278f734a40 --- /dev/null +++ b/pkg/expr/query_convert_test.go @@ -0,0 +1,50 @@ +package expr + +import ( + "testing" + "time" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" + "github.com/stretchr/testify/require" +) + +func TestConvertBackendRequestToDataRequest(t *testing.T) { + input1 := backend.DataQuery{ + RefID: "A", + QueryType: "large", + MaxDataPoints: 42, + Interval: time.Millisecond * 10, + TimeRange: backend.TimeRange{ + From: time.UnixMilli(1753959290000), + To: time.UnixMilli(1753959390000), + }, + JSON: []byte(`{ "field1": "value1" }`), + } + + result1 := data.DataQuery{ + CommonQueryProperties: data.CommonQueryProperties{ + RefID: "A", + QueryType: "large", + MaxDataPoints: 42, + IntervalMS: 10.0, + TimeRange: &data.TimeRange{ + From: "1753959290000", + To: "1753959390000", + }, + }, + } + result1.Set("field1", "value1") + + req := backend.QueryDataRequest{ + Queries: []backend.DataQuery{input1}, + } + + expected := data.QueryDataRequest{ + Queries: []data.DataQuery{result1}, + } + + result, err := ConvertBackendRequestToDataRequest(&req) + require.NoError(t, err) + require.Equal(t, &expected, result) +} diff --git a/pkg/generated/applyconfiguration/utils.go b/pkg/generated/applyconfiguration/utils.go index a512116f25c..d792c0be680 100644 --- a/pkg/generated/applyconfiguration/utils.go +++ b/pkg/generated/applyconfiguration/utils.go @@ -5,11 +5,9 @@ package applyconfiguration import ( - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - servicev0alpha1 "github.com/grafana/grafana/pkg/apis/service/v0alpha1" + v0alpha1 "github.com/grafana/grafana/pkg/apis/service/v0alpha1" internal "github.com/grafana/grafana/pkg/generated/applyconfiguration/internal" - provisioningv0alpha1 "github.com/grafana/grafana/pkg/generated/applyconfiguration/provisioning/v0alpha1" - applyconfigurationservicev0alpha1 "github.com/grafana/grafana/pkg/generated/applyconfiguration/service/v0alpha1" + servicev0alpha1 "github.com/grafana/grafana/pkg/generated/applyconfiguration/service/v0alpha1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" testing "k8s.io/client-go/testing" @@ -19,39 +17,11 @@ import ( // apply configuration type exists for the given GroupVersionKind. func ForKind(kind schema.GroupVersionKind) interface{} { switch kind { - // Group=provisioning.grafana.app, Version=v0alpha1 - case v0alpha1.SchemeGroupVersion.WithKind("BitbucketRepositoryConfig"): - return &provisioningv0alpha1.BitbucketRepositoryConfigApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("GitHubRepositoryConfig"): - return &provisioningv0alpha1.GitHubRepositoryConfigApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("GitLabRepositoryConfig"): - return &provisioningv0alpha1.GitLabRepositoryConfigApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("GitRepositoryConfig"): - return &provisioningv0alpha1.GitRepositoryConfigApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("HealthStatus"): - return &provisioningv0alpha1.HealthStatusApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("LocalRepositoryConfig"): - return &provisioningv0alpha1.LocalRepositoryConfigApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("Repository"): - return &provisioningv0alpha1.RepositoryApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("RepositorySpec"): - return &provisioningv0alpha1.RepositorySpecApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("RepositoryStatus"): - return &provisioningv0alpha1.RepositoryStatusApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("ResourceCount"): - return &provisioningv0alpha1.ResourceCountApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("SyncOptions"): - return &provisioningv0alpha1.SyncOptionsApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("SyncStatus"): - return &provisioningv0alpha1.SyncStatusApplyConfiguration{} - case v0alpha1.SchemeGroupVersion.WithKind("WebhookStatus"): - return &provisioningv0alpha1.WebhookStatusApplyConfiguration{} - - // Group=service.grafana.app, Version=v0alpha1 - case servicev0alpha1.SchemeGroupVersion.WithKind("ExternalName"): - return &applyconfigurationservicev0alpha1.ExternalNameApplyConfiguration{} - case servicev0alpha1.SchemeGroupVersion.WithKind("ExternalNameSpec"): - return &applyconfigurationservicev0alpha1.ExternalNameSpecApplyConfiguration{} + // Group=service.grafana.app, Version=v0alpha1 + case v0alpha1.SchemeGroupVersion.WithKind("ExternalName"): + return &servicev0alpha1.ExternalNameApplyConfiguration{} + case v0alpha1.SchemeGroupVersion.WithKind("ExternalNameSpec"): + return &servicev0alpha1.ExternalNameSpecApplyConfiguration{} } return nil diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go index ad40ccbcfed..972fed70d47 100644 --- a/pkg/generated/clientset/versioned/clientset.go +++ b/pkg/generated/clientset/versioned/clientset.go @@ -8,7 +8,6 @@ import ( fmt "fmt" http "net/http" - provisioningv0alpha1 "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" servicev0alpha1 "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/service/v0alpha1" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" @@ -17,20 +16,13 @@ import ( type Interface interface { Discovery() discovery.DiscoveryInterface - ProvisioningV0alpha1() provisioningv0alpha1.ProvisioningV0alpha1Interface ServiceV0alpha1() servicev0alpha1.ServiceV0alpha1Interface } // Clientset contains the clients for groups. type Clientset struct { *discovery.DiscoveryClient - provisioningV0alpha1 *provisioningv0alpha1.ProvisioningV0alpha1Client - serviceV0alpha1 *servicev0alpha1.ServiceV0alpha1Client -} - -// ProvisioningV0alpha1 retrieves the ProvisioningV0alpha1Client -func (c *Clientset) ProvisioningV0alpha1() provisioningv0alpha1.ProvisioningV0alpha1Interface { - return c.provisioningV0alpha1 + serviceV0alpha1 *servicev0alpha1.ServiceV0alpha1Client } // ServiceV0alpha1 retrieves the ServiceV0alpha1Client @@ -82,10 +74,6 @@ func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, var cs Clientset var err error - cs.provisioningV0alpha1, err = provisioningv0alpha1.NewForConfigAndClient(&configShallowCopy, httpClient) - if err != nil { - return nil, err - } cs.serviceV0alpha1, err = servicev0alpha1.NewForConfigAndClient(&configShallowCopy, httpClient) if err != nil { return nil, err @@ -111,7 +99,6 @@ func NewForConfigOrDie(c *rest.Config) *Clientset { // New creates a new Clientset for the given RESTClient. func New(c rest.Interface) *Clientset { var cs Clientset - cs.provisioningV0alpha1 = provisioningv0alpha1.New(c) cs.serviceV0alpha1 = servicev0alpha1.New(c) cs.DiscoveryClient = discovery.NewDiscoveryClient(c) diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go index a68ce76d23c..7b3cf0c1dad 100644 --- a/pkg/generated/clientset/versioned/fake/clientset_generated.go +++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -7,8 +7,6 @@ package fake import ( applyconfiguration "github.com/grafana/grafana/pkg/generated/applyconfiguration" clientset "github.com/grafana/grafana/pkg/generated/clientset/versioned" - provisioningv0alpha1 "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" - fakeprovisioningv0alpha1 "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake" servicev0alpha1 "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/service/v0alpha1" fakeservicev0alpha1 "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/service/v0alpha1/fake" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -113,11 +111,6 @@ var ( _ testing.FakeClient = &Clientset{} ) -// ProvisioningV0alpha1 retrieves the ProvisioningV0alpha1Client -func (c *Clientset) ProvisioningV0alpha1() provisioningv0alpha1.ProvisioningV0alpha1Interface { - return &fakeprovisioningv0alpha1.FakeProvisioningV0alpha1{Fake: &c.Fake} -} - // ServiceV0alpha1 retrieves the ServiceV0alpha1Client func (c *Clientset) ServiceV0alpha1() servicev0alpha1.ServiceV0alpha1Interface { return &fakeservicev0alpha1.FakeServiceV0alpha1{Fake: &c.Fake} diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go index d64a8572697..bbf12658e76 100644 --- a/pkg/generated/clientset/versioned/fake/register.go +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -5,7 +5,6 @@ package fake import ( - provisioningv0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" servicev0alpha1 "github.com/grafana/grafana/pkg/apis/service/v0alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" @@ -18,7 +17,6 @@ var scheme = runtime.NewScheme() var codecs = serializer.NewCodecFactory(scheme) var localSchemeBuilder = runtime.SchemeBuilder{ - provisioningv0alpha1.AddToScheme, servicev0alpha1.AddToScheme, } diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go index 0aa0fb1be43..dd6e9619160 100644 --- a/pkg/generated/clientset/versioned/scheme/register.go +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -5,7 +5,6 @@ package scheme import ( - provisioningv0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" servicev0alpha1 "github.com/grafana/grafana/pkg/apis/service/v0alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" @@ -18,7 +17,6 @@ var Scheme = runtime.NewScheme() var Codecs = serializer.NewCodecFactory(Scheme) var ParameterCodec = runtime.NewParameterCodec(Scheme) var localSchemeBuilder = runtime.SchemeBuilder{ - provisioningv0alpha1.AddToScheme, servicev0alpha1.AddToScheme, } diff --git a/pkg/generated/informers/externalversions/factory.go b/pkg/generated/informers/externalversions/factory.go index 86ef036dae3..93a498c5d2a 100644 --- a/pkg/generated/informers/externalversions/factory.go +++ b/pkg/generated/informers/externalversions/factory.go @@ -11,7 +11,6 @@ import ( versioned "github.com/grafana/grafana/pkg/generated/clientset/versioned" internalinterfaces "github.com/grafana/grafana/pkg/generated/informers/externalversions/internalinterfaces" - provisioning "github.com/grafana/grafana/pkg/generated/informers/externalversions/provisioning" service "github.com/grafana/grafana/pkg/generated/informers/externalversions/service" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" @@ -241,14 +240,9 @@ type SharedInformerFactory interface { // client. InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer - Provisioning() provisioning.Interface Service() service.Interface } -func (f *sharedInformerFactory) Provisioning() provisioning.Interface { - return provisioning.New(f, f.namespace, f.tweakListOptions) -} - func (f *sharedInformerFactory) Service() service.Interface { return service.New(f, f.namespace, f.tweakListOptions) } diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index 676e6b91f8d..9731bd8cbf2 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -7,8 +7,7 @@ package externalversions import ( fmt "fmt" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - servicev0alpha1 "github.com/grafana/grafana/pkg/apis/service/v0alpha1" + v0alpha1 "github.com/grafana/grafana/pkg/apis/service/v0alpha1" schema "k8s.io/apimachinery/pkg/runtime/schema" cache "k8s.io/client-go/tools/cache" ) @@ -39,12 +38,8 @@ func (f *genericInformer) Lister() cache.GenericLister { // TODO extend this to unknown resources with a client pool func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { - // Group=provisioning.grafana.app, Version=v0alpha1 - case v0alpha1.SchemeGroupVersion.WithResource("repositories"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Provisioning().V0alpha1().Repositories().Informer()}, nil - - // Group=service.grafana.app, Version=v0alpha1 - case servicev0alpha1.SchemeGroupVersion.WithResource("externalnames"): + // Group=service.grafana.app, Version=v0alpha1 + case v0alpha1.SchemeGroupVersion.WithResource("externalnames"): return &genericInformer{resource: resource.GroupResource(), informer: f.Service().V0alpha1().ExternalNames().Informer()}, nil } diff --git a/pkg/registry/apis/provisioning/controller/finalizers.go b/pkg/registry/apis/provisioning/controller/finalizers.go index 554baa86ca9..b30e670a80f 100644 --- a/pkg/registry/apis/provisioning/controller/finalizers.go +++ b/pkg/registry/apis/provisioning/controller/finalizers.go @@ -12,8 +12,8 @@ import ( "github.com/grafana/grafana-app-sdk/logging" folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" ) diff --git a/pkg/registry/apis/provisioning/controller/repository.go b/pkg/registry/apis/provisioning/controller/repository.go index 4b673907e0f..ef5578b1455 100644 --- a/pkg/registry/apis/provisioning/controller/repository.go +++ b/pkg/registry/apis/provisioning/controller/repository.go @@ -17,11 +17,11 @@ import ( "k8s.io/client-go/util/workqueue" "github.com/grafana/grafana-app-sdk/logging" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + client "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" + informer "github.com/grafana/grafana/apps/provisioning/pkg/generated/informers/externalversions/provisioning/v0alpha1" + listers "github.com/grafana/grafana/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - client "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" - informer "github.com/grafana/grafana/pkg/generated/informers/externalversions/provisioning/v0alpha1" - listers "github.com/grafana/grafana/pkg/generated/listers/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" @@ -239,7 +239,7 @@ func (rc *RepositoryController) handleDelete(ctx context.Context, obj *provision Patch(ctx, obj.Name, types.JSONPatchType, []byte(`[ { "op": "remove", "path": "/metadata/finalizers" } ]`), v1.PatchOptions{ - FieldManager: "repository-controller", + FieldManager: "provisioning-controller", }) return err // delete will be called again } diff --git a/pkg/registry/apis/provisioning/controller/status.go b/pkg/registry/apis/provisioning/controller/status.go index 1cbedf57c9b..40ed29624c1 100644 --- a/pkg/registry/apis/provisioning/controller/status.go +++ b/pkg/registry/apis/provisioning/controller/status.go @@ -5,8 +5,8 @@ import ( "encoding/json" "fmt" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - client "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + client "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) diff --git a/pkg/registry/apis/provisioning/controller/status_test.go b/pkg/registry/apis/provisioning/controller/status_test.go index 56354c2c165..77e7f737ea5 100644 --- a/pkg/registry/apis/provisioning/controller/status_test.go +++ b/pkg/registry/apis/provisioning/controller/status_test.go @@ -6,8 +6,8 @@ import ( "fmt" "testing" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1/fake" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" diff --git a/pkg/registry/apis/provisioning/extra.go b/pkg/registry/apis/provisioning/extra.go index 0c5f9e921dd..e7f888a914b 100644 --- a/pkg/registry/apis/provisioning/extra.go +++ b/pkg/registry/apis/provisioning/extra.go @@ -3,7 +3,7 @@ package provisioning import ( "context" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/controller" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" diff --git a/pkg/registry/apis/provisioning/files.go b/pkg/registry/apis/provisioning/files.go index 4dd6604c989..1df860b9738 100644 --- a/pkg/registry/apis/provisioning/files.go +++ b/pkg/registry/apis/provisioning/files.go @@ -12,8 +12,8 @@ import ( authlib "github.com/grafana/authlib/types" "github.com/grafana/grafana-app-sdk/logging" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" "github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath" diff --git a/pkg/registry/apis/provisioning/history.go b/pkg/registry/apis/provisioning/history.go index e177295ca23..7770e44fe6d 100644 --- a/pkg/registry/apis/provisioning/history.go +++ b/pkg/registry/apis/provisioning/history.go @@ -11,7 +11,7 @@ import ( "k8s.io/apiserver/pkg/registry/rest" "github.com/grafana/grafana-app-sdk/logging" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath" ) diff --git a/pkg/registry/apis/provisioning/jobs.go b/pkg/registry/apis/provisioning/jobs.go index d19dd61722c..e563cc94fa9 100644 --- a/pkg/registry/apis/provisioning/jobs.go +++ b/pkg/registry/apis/provisioning/jobs.go @@ -11,7 +11,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/registry/rest" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" ) diff --git a/pkg/registry/apis/provisioning/jobs/delete/worker.go b/pkg/registry/apis/provisioning/jobs/delete/worker.go index c1f6739405e..63db00ef224 100644 --- a/pkg/registry/apis/provisioning/jobs/delete/worker.go +++ b/pkg/registry/apis/provisioning/jobs/delete/worker.go @@ -8,7 +8,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/jobs/delete/worker_test.go b/pkg/registry/apis/provisioning/jobs/delete/worker_test.go index 5d63687dd2c..7f2fa3c22bb 100644 --- a/pkg/registry/apis/provisioning/jobs/delete/worker_test.go +++ b/pkg/registry/apis/provisioning/jobs/delete/worker_test.go @@ -9,7 +9,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/jobs/driver.go b/pkg/registry/apis/provisioning/jobs/driver.go index 8d31d1dde1a..988422aacff 100644 --- a/pkg/registry/apis/provisioning/jobs/driver.go +++ b/pkg/registry/apis/provisioning/jobs/driver.go @@ -9,8 +9,8 @@ import ( "k8s.io/apiserver/pkg/endpoints/request" "github.com/grafana/grafana-app-sdk/logging" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/apifmt" ) diff --git a/pkg/registry/apis/provisioning/jobs/export/all.go b/pkg/registry/apis/provisioning/jobs/export/all.go index 1010c02f630..c2cb0f85eb5 100644 --- a/pkg/registry/apis/provisioning/jobs/export/all.go +++ b/pkg/registry/apis/provisioning/jobs/export/all.go @@ -3,7 +3,7 @@ package export import ( "context" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" ) diff --git a/pkg/registry/apis/provisioning/jobs/export/folders.go b/pkg/registry/apis/provisioning/jobs/export/folders.go index afc7db05a87..48a4f7839ae 100644 --- a/pkg/registry/apis/provisioning/jobs/export/folders.go +++ b/pkg/registry/apis/provisioning/jobs/export/folders.go @@ -8,8 +8,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/client-go/dynamic" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" @@ -32,8 +32,9 @@ func ExportFolders(ctx context.Context, repoName string, options provisioning.Ex } manager, _ := meta.GetManagerProperties() - if manager.Identity == repoName { - return nil // skip it... already in tree? + // Skip if already managed by any manager (repository, file provisioning, etc.) + if manager.Identity != "" { + return nil } return tree.AddUnstructured(item) diff --git a/pkg/registry/apis/provisioning/jobs/export/folders_test.go b/pkg/registry/apis/provisioning/jobs/export/folders_test.go index bec0214d7ef..2eaeeabd373 100644 --- a/pkg/registry/apis/provisioning/jobs/export/folders_test.go +++ b/pkg/registry/apis/provisioning/jobs/export/folders_test.go @@ -6,8 +6,8 @@ import ( "fmt" "testing" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" @@ -298,38 +298,21 @@ func TestExportFolders(t *testing.T) { progress.On("SetMessage", mock.Anything, "read folder tree from API server").Return() progress.On("SetMessage", mock.Anything, "write folders to repository").Return() progress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool { - return result.Name == "parent-uid" && result.Action == repository.FileActionCreated + return result.Name == "parent-folder" && result.Action == repository.FileActionCreated })).Return() progress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool { - return result.Name == "child-uid" && result.Action == repository.FileActionCreated + return result.Name == "child-folder" && result.Action == repository.FileActionCreated })).Return() progress.On("TooManyErrors").Return(nil) progress.On("TooManyErrors").Return(nil) }, setupResources: func(repoResources *resources.MockRepositoryResources) { repoResources.On("EnsureFolderTreeExists", mock.Anything, "feature/branch", "grafana", mock.MatchedBy(func(tree resources.FolderTree) bool { - expectedFolders := []resources.Folder{ - {ID: "parent-folder", Path: "parent-folder"}, - {ID: "child-folder", Path: "parent-folder/child-folder"}, - } - - if tree.Count() != len(expectedFolders) { - return false - } - - for _, folder := range expectedFolders { - dir, ok := tree.DirPath(folder.ID, "") - if !ok || dir.Path != folder.Path { - return false - } - } - - return true + return tree.Count() == 2 }), mock.MatchedBy(func(fn func(folder resources.Folder, created bool, err error) error) bool { - // Parent folder should be processed first - require.NoError(t, fn(resources.Folder{ID: "parent-uid", Path: "grafana/parent-folder"}, true, nil)) - // Then child folder with nested path - require.NoError(t, fn(resources.Folder{ID: "child-uid", Path: "grafana/parent-folder/child-folder"}, true, nil)) + require.NoError(t, fn(resources.Folder{ID: "parent-folder", Path: "grafana/parent-folder"}, true, nil)) + require.NoError(t, fn(resources.Folder{ID: "child-folder", Path: "grafana/parent-folder/child-folder"}, true, nil)) + return true })).Return(nil) }, @@ -380,7 +363,7 @@ func TestExportFolders(t *testing.T) { } func TestFolderMetaAccessor(t *testing.T) { - t.Run("should export folders from another manager", func(t *testing.T) { + t.Run("should skip folders from another manager", func(t *testing.T) { obj := &unstructured.Unstructured{ Object: map[string]interface{}{ "metadata": map[string]interface{}{ @@ -405,21 +388,12 @@ func TestFolderMetaAccessor(t *testing.T) { mockRepoResources := resources.NewMockRepositoryResources(t) mockRepoResources.On("EnsureFolderTreeExists", mock.Anything, "feature/branch", "grafana", mock.MatchedBy(func(tree resources.FolderTree) bool { - return tree.Count() == 1 - }), mock.MatchedBy(func(fn func(folder resources.Folder, created bool, err error) error) bool { - require.NoError(t, fn(resources.Folder{ID: "test-folder-uid", Path: "grafana/test-folder"}, true, nil)) - return true - })).Return(nil) + return tree.Count() == 0 // Should be 0 since folder is managed by other manager + }), mock.Anything).Return(nil) progress := jobs.NewMockJobProgressRecorder(t) - progress.On("SetMessage", mock.Anything, mock.Anything).Return() - progress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool { - return result.Action == repository.FileActionCreated && - result.Name == "test-folder-uid" && - result.Error == nil && - result.Path == "grafana/test-folder" - })).Return() - progress.On("TooManyErrors").Return(nil) + progress.On("SetMessage", mock.Anything, mock.Anything).Return().Twice() + // No Record calls expected since folder should be skipped err = ExportFolders(context.Background(), "test-repo", v0alpha1.ExportJobOptions{ Path: "grafana", Branch: "feature/branch", @@ -430,7 +404,7 @@ func TestFolderMetaAccessor(t *testing.T) { mockRepoResources.AssertExpectations(t) progress.AssertExpectations(t) }) - t.Run("should skip if repo is the manager", func(t *testing.T) { + t.Run("should skip if current repo is the manager", func(t *testing.T) { obj := &unstructured.Unstructured{ Object: map[string]interface{}{ "metadata": map[string]interface{}{ @@ -492,6 +466,45 @@ func TestFolderMetaAccessor(t *testing.T) { mockRepoResources.AssertExpectations(t) progress.AssertExpectations(t) }) + t.Run("should skip if managed by any other manager", func(t *testing.T) { + obj := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "metadata": map[string]interface{}{ + "name": "test-folder", + "annotations": map[string]interface{}{ + "folder.grafana.app/uid": "test-folder-uid", + }, + }, + }, + } + meta, err := utils.MetaAccessor(obj) + require.NoError(t, err) + meta.SetManagerProperties(utils.ManagerProperties{ + Kind: utils.ManagerKindTerraform, + Identity: "terraform-provisioning", + AllowsEdits: false, + Suspended: false, + }) + fakeFolderClient := &mockDynamicInterface{ + items: []unstructured.Unstructured{*obj}, + } + + mockRepoResources := resources.NewMockRepositoryResources(t) + progress := jobs.NewMockJobProgressRecorder(t) + progress.On("SetMessage", mock.Anything, mock.Anything).Return().Twice() + mockRepoResources.On("EnsureFolderTreeExists", mock.Anything, "feature/branch", "grafana", mock.MatchedBy(func(tree resources.FolderTree) bool { + return tree.Count() == 0 // Should be empty since folder was skipped + }), mock.Anything).Return(nil) + + err = ExportFolders(context.Background(), "test-repo", v0alpha1.ExportJobOptions{ + Path: "grafana", + Branch: "feature/branch", + }, fakeFolderClient, mockRepoResources, progress) + + require.NoError(t, err) + mockRepoResources.AssertExpectations(t) + progress.AssertExpectations(t) + }) } // mockDynamicInterface implements a simplified version of the dynamic.ResourceInterface diff --git a/pkg/registry/apis/provisioning/jobs/export/mock_export_fn.go b/pkg/registry/apis/provisioning/jobs/export/mock_export_fn.go index e861e7da3c3..e748228da2b 100644 --- a/pkg/registry/apis/provisioning/jobs/export/mock_export_fn.go +++ b/pkg/registry/apis/provisioning/jobs/export/mock_export_fn.go @@ -10,7 +10,7 @@ import ( resources "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockExportFn is an autogenerated mock type for the ExportFn type diff --git a/pkg/registry/apis/provisioning/jobs/export/resources.go b/pkg/registry/apis/provisioning/jobs/export/resources.go index b986392c3d6..8b1baff08ca 100644 --- a/pkg/registry/apis/provisioning/jobs/export/resources.go +++ b/pkg/registry/apis/provisioning/jobs/export/resources.go @@ -10,7 +10,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/client-go/dynamic" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" @@ -103,6 +104,23 @@ func exportResource(ctx context.Context, Action: repository.FileActionCreated, } + // Check if resource is already managed by a repository + meta, err := utils.MetaAccessor(item) + if err != nil { + result.Action = repository.FileActionIgnored + result.Error = fmt.Errorf("extract meta accessor: %w", err) + progress.Record(ctx, result) + return nil + } + + manager, _ := meta.GetManagerProperties() + // Skip if already managed by any manager (repository, file provisioning, etc.) + if manager.Identity != "" { + result.Action = repository.FileActionIgnored + progress.Record(ctx, result) + return nil + } + if shim != nil { item, err = shim(ctx, item) } diff --git a/pkg/registry/apis/provisioning/jobs/export/resources_test.go b/pkg/registry/apis/provisioning/jobs/export/resources_test.go index db8209cf414..8be32cebed9 100644 --- a/pkg/registry/apis/provisioning/jobs/export/resources_test.go +++ b/pkg/registry/apis/provisioning/jobs/export/resources_test.go @@ -5,12 +5,13 @@ import ( "fmt" "testing" + "github.com/grafana/grafana/pkg/apimachinery/utils" mock "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" - provisioningV0 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioningV0 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" @@ -532,3 +533,37 @@ func TestExportResources_Dashboards_V2beta1_ClientError(t *testing.T) { err := runExportTest(t, mockItems, setupProgress, setupResources) require.NoError(t, err) } + +func TestExportResources_Dashboards_SkipsManagedResources(t *testing.T) { + // Create a dashboard managed by file provisioning + dashboard := createDashboardObject("managed-dashboard") + + // Add manager metadata using utils package + meta, err := utils.MetaAccessor(&dashboard) + require.NoError(t, err) + meta.SetManagerProperties(utils.ManagerProperties{ + Kind: utils.ManagerKindTerraform, + Identity: "terraform-provisioning", + AllowsEdits: false, + Suspended: false, + }) + + mockItems := []unstructured.Unstructured{dashboard} + + setupProgress := func(progress *jobs.MockJobProgressRecorder) { + progress.On("SetMessage", mock.Anything, "start resource export").Return() + progress.On("SetMessage", mock.Anything, "export dashboards").Return() + progress.On("Record", mock.Anything, mock.MatchedBy(func(result jobs.JobResourceResult) bool { + return result.Name == "managed-dashboard" && result.Action == repository.FileActionIgnored + })).Return() + progress.On("TooManyErrors").Return(nil).Maybe() + } + + setupResources := func(repoResources *resources.MockRepositoryResources, resourceClients *resources.MockResourceClients, mockClient *mockDynamicInterface, gvk schema.GroupVersionKind) { + resourceClients.On("ForResource", resources.DashboardResource).Return(mockClient, gvk, nil) + // No WriteResourceFileFromObject call expected since resource should be skipped + } + + err = runExportTest(t, mockItems, setupProgress, setupResources) + require.NoError(t, err) +} diff --git a/pkg/registry/apis/provisioning/jobs/export/worker.go b/pkg/registry/apis/provisioning/jobs/export/worker.go index 13536a62aa5..524b01dce4c 100644 --- a/pkg/registry/apis/provisioning/jobs/export/worker.go +++ b/pkg/registry/apis/provisioning/jobs/export/worker.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/jobs/export/worker_test.go b/pkg/registry/apis/provisioning/jobs/export/worker_test.go index 930b5f051a9..d78b0ec5b8b 100644 --- a/pkg/registry/apis/provisioning/jobs/export/worker_test.go +++ b/pkg/registry/apis/provisioning/jobs/export/worker_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/jobs/history.go b/pkg/registry/apis/provisioning/jobs/history.go index 88351e12bad..0a7897319f9 100644 --- a/pkg/registry/apis/provisioning/jobs/history.go +++ b/pkg/registry/apis/provisioning/jobs/history.go @@ -7,7 +7,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // History keeps track of completed jobs diff --git a/pkg/registry/apis/provisioning/jobs/history_mock.go b/pkg/registry/apis/provisioning/jobs/history_mock.go index dbb61568566..f6697d33bcf 100644 --- a/pkg/registry/apis/provisioning/jobs/history_mock.go +++ b/pkg/registry/apis/provisioning/jobs/history_mock.go @@ -5,7 +5,7 @@ package jobs import ( context "context" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" mock "github.com/stretchr/testify/mock" ) diff --git a/pkg/registry/apis/provisioning/jobs/job_progress_recorder_mock.go b/pkg/registry/apis/provisioning/jobs/job_progress_recorder_mock.go index 54b07e6a38a..be0b6d3d9b0 100644 --- a/pkg/registry/apis/provisioning/jobs/job_progress_recorder_mock.go +++ b/pkg/registry/apis/provisioning/jobs/job_progress_recorder_mock.go @@ -5,7 +5,7 @@ package jobs import ( context "context" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" mock "github.com/stretchr/testify/mock" ) diff --git a/pkg/registry/apis/provisioning/jobs/migrate/legacy.go b/pkg/registry/apis/provisioning/jobs/migrate/legacy.go index 8b253bebd7f..929c7a6b9cf 100644 --- a/pkg/registry/apis/provisioning/jobs/migrate/legacy.go +++ b/pkg/registry/apis/provisioning/jobs/migrate/legacy.go @@ -7,7 +7,7 @@ import ( "time" "github.com/grafana/grafana-app-sdk/logging" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/jobs/migrate/legacy_resources.go b/pkg/registry/apis/provisioning/jobs/migrate/legacy_resources.go index 34391f3e56f..03711c485d6 100644 --- a/pkg/registry/apis/provisioning/jobs/migrate/legacy_resources.go +++ b/pkg/registry/apis/provisioning/jobs/migrate/legacy_resources.go @@ -6,8 +6,8 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs/export" diff --git a/pkg/registry/apis/provisioning/jobs/migrate/legacy_resources_test.go b/pkg/registry/apis/provisioning/jobs/migrate/legacy_resources_test.go index aff308a4a9b..88e60341f6b 100644 --- a/pkg/registry/apis/provisioning/jobs/migrate/legacy_resources_test.go +++ b/pkg/registry/apis/provisioning/jobs/migrate/legacy_resources_test.go @@ -13,8 +13,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs/export" diff --git a/pkg/registry/apis/provisioning/jobs/migrate/legacy_test.go b/pkg/registry/apis/provisioning/jobs/migrate/legacy_test.go index 3b25de7aeaf..319984391d6 100644 --- a/pkg/registry/apis/provisioning/jobs/migrate/legacy_test.go +++ b/pkg/registry/apis/provisioning/jobs/migrate/legacy_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/jobs/migrate/mock_legacy_resources_migrator.go b/pkg/registry/apis/provisioning/jobs/migrate/mock_legacy_resources_migrator.go index 4a0d27fafb5..4e70afef693 100644 --- a/pkg/registry/apis/provisioning/jobs/migrate/mock_legacy_resources_migrator.go +++ b/pkg/registry/apis/provisioning/jobs/migrate/mock_legacy_resources_migrator.go @@ -10,7 +10,7 @@ import ( repository "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockLegacyResourcesMigrator is an autogenerated mock type for the LegacyResourcesMigrator type diff --git a/pkg/registry/apis/provisioning/jobs/migrate/mock_migrator.go b/pkg/registry/apis/provisioning/jobs/migrate/mock_migrator.go index 0128ce741f4..685c5ab7c47 100644 --- a/pkg/registry/apis/provisioning/jobs/migrate/mock_migrator.go +++ b/pkg/registry/apis/provisioning/jobs/migrate/mock_migrator.go @@ -10,7 +10,7 @@ import ( repository "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockMigrator is an autogenerated mock type for the Migrator type diff --git a/pkg/registry/apis/provisioning/jobs/migrate/unifiedstorage.go b/pkg/registry/apis/provisioning/jobs/migrate/unifiedstorage.go index 4f9f33b96ec..aa6ce0b596d 100644 --- a/pkg/registry/apis/provisioning/jobs/migrate/unifiedstorage.go +++ b/pkg/registry/apis/provisioning/jobs/migrate/unifiedstorage.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/jobs/migrate/unifiedstorage_test.go b/pkg/registry/apis/provisioning/jobs/migrate/unifiedstorage_test.go index 3cb48119842..5bc028f138d 100644 --- a/pkg/registry/apis/provisioning/jobs/migrate/unifiedstorage_test.go +++ b/pkg/registry/apis/provisioning/jobs/migrate/unifiedstorage_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/registry/apis/provisioning/jobs/migrate/worker.go b/pkg/registry/apis/provisioning/jobs/migrate/worker.go index 625b49ea32c..1293f18afa4 100644 --- a/pkg/registry/apis/provisioning/jobs/migrate/worker.go +++ b/pkg/registry/apis/provisioning/jobs/migrate/worker.go @@ -4,7 +4,7 @@ import ( "context" "errors" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/storage/legacysql/dualwrite" diff --git a/pkg/registry/apis/provisioning/jobs/migrate/worker_test.go b/pkg/registry/apis/provisioning/jobs/migrate/worker_test.go index e7d8c7c6175..3993b422492 100644 --- a/pkg/registry/apis/provisioning/jobs/migrate/worker_test.go +++ b/pkg/registry/apis/provisioning/jobs/migrate/worker_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/local" diff --git a/pkg/registry/apis/provisioning/jobs/move/worker.go b/pkg/registry/apis/provisioning/jobs/move/worker.go index 5c2bba7e4e4..b2ff1912bd5 100644 --- a/pkg/registry/apis/provisioning/jobs/move/worker.go +++ b/pkg/registry/apis/provisioning/jobs/move/worker.go @@ -9,7 +9,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/jobs/move/worker_test.go b/pkg/registry/apis/provisioning/jobs/move/worker_test.go index 3be6d2d4619..c398cedc830 100644 --- a/pkg/registry/apis/provisioning/jobs/move/worker_test.go +++ b/pkg/registry/apis/provisioning/jobs/move/worker_test.go @@ -11,7 +11,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/jobs/persistentstore.go b/pkg/registry/apis/provisioning/jobs/persistentstore.go index abfdf2cecc7..e172e106ecf 100644 --- a/pkg/registry/apis/provisioning/jobs/persistentstore.go +++ b/pkg/registry/apis/provisioning/jobs/persistentstore.go @@ -18,8 +18,8 @@ import ( "k8s.io/apiserver/pkg/registry/rest" "github.com/grafana/grafana-app-sdk/logging" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/apifmt" ) diff --git a/pkg/registry/apis/provisioning/jobs/progress.go b/pkg/registry/apis/provisioning/jobs/progress.go index 081ac9a3b86..4f7bb2ad53d 100644 --- a/pkg/registry/apis/provisioning/jobs/progress.go +++ b/pkg/registry/apis/provisioning/jobs/progress.go @@ -7,7 +7,7 @@ import ( "time" "github.com/grafana/grafana-app-sdk/logging" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/jobs/progress_fn_mock.go b/pkg/registry/apis/provisioning/jobs/progress_fn_mock.go index fd634d60844..8c68bec33f7 100644 --- a/pkg/registry/apis/provisioning/jobs/progress_fn_mock.go +++ b/pkg/registry/apis/provisioning/jobs/progress_fn_mock.go @@ -5,7 +5,7 @@ package jobs import ( context "context" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" mock "github.com/stretchr/testify/mock" ) diff --git a/pkg/registry/apis/provisioning/jobs/queue.go b/pkg/registry/apis/provisioning/jobs/queue.go index 32b935e4be8..3a9757d93a1 100644 --- a/pkg/registry/apis/provisioning/jobs/queue.go +++ b/pkg/registry/apis/provisioning/jobs/queue.go @@ -3,7 +3,7 @@ package jobs import ( "context" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/jobs/queue_mock.go b/pkg/registry/apis/provisioning/jobs/queue_mock.go index 41552749cad..37fd66f214b 100644 --- a/pkg/registry/apis/provisioning/jobs/queue_mock.go +++ b/pkg/registry/apis/provisioning/jobs/queue_mock.go @@ -5,7 +5,7 @@ package jobs import ( context "context" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" mock "github.com/stretchr/testify/mock" ) diff --git a/pkg/registry/apis/provisioning/jobs/store_mock.go b/pkg/registry/apis/provisioning/jobs/store_mock.go index 8a6bb364f80..114a621561e 100644 --- a/pkg/registry/apis/provisioning/jobs/store_mock.go +++ b/pkg/registry/apis/provisioning/jobs/store_mock.go @@ -5,7 +5,7 @@ package jobs import ( context "context" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" mock "github.com/stretchr/testify/mock" ) diff --git a/pkg/registry/apis/provisioning/jobs/sync/changes.go b/pkg/registry/apis/provisioning/jobs/sync/changes.go index 4c1f5db9136..4555e670a47 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/changes.go +++ b/pkg/registry/apis/provisioning/jobs/sync/changes.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" "github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath" diff --git a/pkg/registry/apis/provisioning/jobs/sync/changes_test.go b/pkg/registry/apis/provisioning/jobs/sync/changes_test.go index d2a31696688..d14e9f0c872 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/changes_test.go +++ b/pkg/registry/apis/provisioning/jobs/sync/changes_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" ) diff --git a/pkg/registry/apis/provisioning/jobs/sync/full_test.go b/pkg/registry/apis/provisioning/jobs/sync/full_test.go index 7524fe27f2a..3885700b331 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/full_test.go +++ b/pkg/registry/apis/provisioning/jobs/sync/full_test.go @@ -6,7 +6,7 @@ import ( "fmt" "testing" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/jobs/sync/repository_patch_fn_mock.go b/pkg/registry/apis/provisioning/jobs/sync/repository_patch_fn_mock.go index fb44f7eda29..97a5c70c05b 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/repository_patch_fn_mock.go +++ b/pkg/registry/apis/provisioning/jobs/sync/repository_patch_fn_mock.go @@ -5,7 +5,7 @@ package sync import ( context "context" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" mock "github.com/stretchr/testify/mock" ) diff --git a/pkg/registry/apis/provisioning/jobs/sync/sync.go b/pkg/registry/apis/provisioning/jobs/sync/sync.go index 9aca33ebcd3..a82423ecbd7 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/sync.go +++ b/pkg/registry/apis/provisioning/jobs/sync/sync.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/jobs/sync/sync_test.go b/pkg/registry/apis/provisioning/jobs/sync/sync_test.go index f7bb4e0c961..c0398925b04 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/sync_test.go +++ b/pkg/registry/apis/provisioning/jobs/sync/sync_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/jobs/sync/syncer_mock.go b/pkg/registry/apis/provisioning/jobs/sync/syncer_mock.go index 3eb3e4de7bb..0dedab34cc1 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/syncer_mock.go +++ b/pkg/registry/apis/provisioning/jobs/sync/syncer_mock.go @@ -12,7 +12,7 @@ import ( resources "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockSyncer is an autogenerated mock type for the Syncer type diff --git a/pkg/registry/apis/provisioning/jobs/sync/worker.go b/pkg/registry/apis/provisioning/jobs/sync/worker.go index 5b8eb701b34..66e96673051 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/worker.go +++ b/pkg/registry/apis/provisioning/jobs/sync/worker.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/grafana/grafana-app-sdk/logging" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/jobs/sync/worker_test.go b/pkg/registry/apis/provisioning/jobs/sync/worker_test.go index 02e152afb6a..5b8bf67166f 100644 --- a/pkg/registry/apis/provisioning/jobs/sync/worker_test.go +++ b/pkg/registry/apis/provisioning/jobs/sync/worker_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/jobs/worker_mock.go b/pkg/registry/apis/provisioning/jobs/worker_mock.go index b264b258d1c..60a710016d5 100644 --- a/pkg/registry/apis/provisioning/jobs/worker_mock.go +++ b/pkg/registry/apis/provisioning/jobs/worker_mock.go @@ -8,7 +8,7 @@ import ( repository "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" mock "github.com/stretchr/testify/mock" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockWorker is an autogenerated mock type for the Worker type diff --git a/pkg/registry/apis/provisioning/list.go b/pkg/registry/apis/provisioning/list.go index 202a063736f..ddd851b1bb7 100644 --- a/pkg/registry/apis/provisioning/list.go +++ b/pkg/registry/apis/provisioning/list.go @@ -10,7 +10,7 @@ import ( "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" ) diff --git a/pkg/registry/apis/provisioning/refs.go b/pkg/registry/apis/provisioning/refs.go index 47fe2b24324..8eed640fdd1 100644 --- a/pkg/registry/apis/provisioning/refs.go +++ b/pkg/registry/apis/provisioning/refs.go @@ -9,7 +9,7 @@ import ( "k8s.io/apiserver/pkg/registry/rest" "github.com/grafana/grafana-app-sdk/logging" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/register.go b/pkg/registry/apis/provisioning/register.go index 9bccd675dd5..48656eba416 100644 --- a/pkg/registry/apis/provisioning/register.go +++ b/pkg/registry/apis/provisioning/register.go @@ -29,15 +29,15 @@ import ( "github.com/grafana/grafana-app-sdk/logging" dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + clientset "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned" + client "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" + informers "github.com/grafana/grafana/apps/provisioning/pkg/generated/informers/externalversions" + listers "github.com/grafana/grafana/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" apiutils "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apiserver/readonly" grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" - clientset "github.com/grafana/grafana/pkg/generated/clientset/versioned" - client "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" - informers "github.com/grafana/grafana/pkg/generated/informers/externalversions" - listers "github.com/grafana/grafana/pkg/generated/listers/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy" @@ -680,7 +680,7 @@ func (b *APIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, err repoprefix := root + "namespaces/{namespace}/repositories/{name}" defs := b.GetOpenAPIDefinitions()(func(path string) spec.Ref { return spec.Ref{} }) - defsBase := "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1." + defsBase := "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1." refsBase := "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1." sub := oas.Paths.Paths[repoprefix+"/test"] diff --git a/pkg/registry/apis/provisioning/repository/config_repository_mock.go b/pkg/registry/apis/provisioning/repository/config_repository_mock.go index 40ea3dcd3de..38d2f4617ca 100644 --- a/pkg/registry/apis/provisioning/repository/config_repository_mock.go +++ b/pkg/registry/apis/provisioning/repository/config_repository_mock.go @@ -8,7 +8,7 @@ import ( mock "github.com/stretchr/testify/mock" field "k8s.io/apimachinery/pkg/util/validation/field" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockConfigRepository is an autogenerated mock type for the Repository type diff --git a/pkg/registry/apis/provisioning/repository/git/git_repository_mock.go b/pkg/registry/apis/provisioning/repository/git/git_repository_mock.go index 4b914b50f19..81b4eecf671 100644 --- a/pkg/registry/apis/provisioning/repository/git/git_repository_mock.go +++ b/pkg/registry/apis/provisioning/repository/git/git_repository_mock.go @@ -10,7 +10,7 @@ import ( repository "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockGitRepository is an autogenerated mock type for the GitRepository type diff --git a/pkg/registry/apis/provisioning/repository/git/mutator.go b/pkg/registry/apis/provisioning/repository/git/mutator.go index e68940fafb8..c4eded27f44 100644 --- a/pkg/registry/apis/provisioning/repository/git/mutator.go +++ b/pkg/registry/apis/provisioning/repository/git/mutator.go @@ -7,7 +7,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/controller" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" ) diff --git a/pkg/registry/apis/provisioning/repository/git/mutator_test.go b/pkg/registry/apis/provisioning/repository/git/mutator_test.go index 87196d6bcb0..de52ab0c0f6 100644 --- a/pkg/registry/apis/provisioning/repository/git/mutator_test.go +++ b/pkg/registry/apis/provisioning/repository/git/mutator_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/registry/apis/provisioning/repository/git/repository.go b/pkg/registry/apis/provisioning/repository/git/repository.go index 5bcdb12cd94..41c3a328788 100644 --- a/pkg/registry/apis/provisioning/repository/git/repository.go +++ b/pkg/registry/apis/provisioning/repository/git/repository.go @@ -16,7 +16,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" "github.com/grafana/grafana-app-sdk/logging" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" @@ -790,6 +790,10 @@ func (r *gitRepository) createSignature(ctx context.Context) (nanogit.Author, na func (r *gitRepository) commit(ctx context.Context, writer nanogit.StagedWriter, comment string) error { author, committer := r.createSignature(ctx) if _, err := writer.Commit(ctx, comment, author, committer); err != nil { + if errors.Is(err, nanogit.ErrNothingToCommit) { + return repository.ErrNothingToCommit + } + return fmt.Errorf("commit changes: %w", err) } return nil diff --git a/pkg/registry/apis/provisioning/repository/git/repository_test.go b/pkg/registry/apis/provisioning/repository/git/repository_test.go index f70346825f6..4236352f94c 100644 --- a/pkg/registry/apis/provisioning/repository/git/repository_test.go +++ b/pkg/registry/apis/provisioning/repository/git/repository_test.go @@ -12,7 +12,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/validation/field" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" "github.com/grafana/nanogit" diff --git a/pkg/registry/apis/provisioning/repository/git/staged.go b/pkg/registry/apis/provisioning/repository/git/staged.go index a3f79567029..f55eb709341 100644 --- a/pkg/registry/apis/provisioning/repository/git/staged.go +++ b/pkg/registry/apis/provisioning/repository/git/staged.go @@ -219,12 +219,24 @@ func (r *stagedGitRepository) Push(ctx context.Context) error { if message == "" { message = "Staged changes" } + if err := r.commit(ctx, r.writer, message); err != nil { return err } } - return r.writer.Push(ctx) + err := r.writer.Push(ctx) + if err != nil { + // Convert nanogit-specific errors to repository-level errors to avoid leaky abstraction + if errors.Is(err, nanogit.ErrNothingToPush) { + return repository.ErrNothingToPush + } + if errors.Is(err, nanogit.ErrNothingToCommit) { + return repository.ErrNothingToCommit + } + return err + } + return nil } func (r *stagedGitRepository) Remove(ctx context.Context) error { diff --git a/pkg/registry/apis/provisioning/repository/git/staged_test.go b/pkg/registry/apis/provisioning/repository/git/staged_test.go index 4501a087e7a..2b18fd3445f 100644 --- a/pkg/registry/apis/provisioning/repository/git/staged_test.go +++ b/pkg/registry/apis/provisioning/repository/git/staged_test.go @@ -3,11 +3,12 @@ package git import ( "context" "errors" + "fmt" "strings" "testing" "time" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/nanogit" "github.com/grafana/nanogit/mocks" @@ -995,6 +996,54 @@ func TestStagedGitRepository_Push(t *testing.T) { expectPushCalls: 1, expectCommitCalls: 1, }, + { + name: "returns repository ErrNothingToPush when nanogit returns ErrNothingToPush", + opts: repository.StageOptions{}, + setupMock: func(mockWriter *mocks.FakeStagedWriter) { + mockWriter.PushReturns(nanogit.ErrNothingToPush) + }, + wantError: repository.ErrNothingToPush, + expectPushCalls: 1, + expectCommitCalls: 0, + }, + { + name: "returns repository ErrNothingToCommit when nanogit returns ErrNothingToCommit", + opts: repository.StageOptions{ + Mode: repository.StageModeCommitOnlyOnce, + }, + setupMock: func(mockWriter *mocks.FakeStagedWriter) { + mockWriter.CommitReturns(nil, nanogit.ErrNothingToCommit) + }, + wantError: repository.ErrNothingToCommit, + expectPushCalls: 0, + expectCommitCalls: 1, + }, + { + name: "returns repository ErrNothingToPush when nanogit returns wrapped ErrNothingToPush", + opts: repository.StageOptions{}, + setupMock: func(mockWriter *mocks.FakeStagedWriter) { + // Use fmt.Errorf with %w to create a wrapped error that errors.Is can detect + wrappedErr := fmt.Errorf("git operation failed: %w", nanogit.ErrNothingToPush) + mockWriter.PushReturns(wrappedErr) + }, + wantError: repository.ErrNothingToPush, + expectPushCalls: 1, + expectCommitCalls: 0, + }, + { + name: "returns repository ErrNothingToCommit when nanogit returns wrapped ErrNothingToCommit", + opts: repository.StageOptions{ + Mode: repository.StageModeCommitOnlyOnce, + }, + setupMock: func(mockWriter *mocks.FakeStagedWriter) { + // Use fmt.Errorf with %w to create a wrapped error that errors.Is can detect + wrappedErr := fmt.Errorf("git operation failed: %w", nanogit.ErrNothingToCommit) + mockWriter.CommitReturns(nil, wrappedErr) + }, + wantError: repository.ErrNothingToCommit, + expectPushCalls: 0, + expectCommitCalls: 1, + }, } for _, tt := range tests { @@ -1007,7 +1056,12 @@ func TestStagedGitRepository_Push(t *testing.T) { err := stagedRepo.Push(context.Background()) if tt.wantError != nil { - require.EqualError(t, err, tt.wantError.Error()) + // For nanogit error conversion tests, use ErrorIs to verify type conversion + if errors.Is(tt.wantError, repository.ErrNothingToPush) || errors.Is(tt.wantError, repository.ErrNothingToCommit) { + require.ErrorIs(t, err, tt.wantError) + } else { + require.EqualError(t, err, tt.wantError.Error()) + } } else { require.NoError(t, err) } diff --git a/pkg/registry/apis/provisioning/repository/github/github_repository_mock.go b/pkg/registry/apis/provisioning/repository/github/github_repository_mock.go index 5ccd5d2e319..7b5509935f1 100644 --- a/pkg/registry/apis/provisioning/repository/github/github_repository_mock.go +++ b/pkg/registry/apis/provisioning/repository/github/github_repository_mock.go @@ -10,7 +10,7 @@ import ( repository "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockGithubRepository is an autogenerated mock type for the GithubRepository type diff --git a/pkg/registry/apis/provisioning/repository/github/mutator.go b/pkg/registry/apis/provisioning/repository/github/mutator.go index 4aec95aa7d6..b0ee0e54a24 100644 --- a/pkg/registry/apis/provisioning/repository/github/mutator.go +++ b/pkg/registry/apis/provisioning/repository/github/mutator.go @@ -6,7 +6,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/controller" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" ) diff --git a/pkg/registry/apis/provisioning/repository/github/mutator_test.go b/pkg/registry/apis/provisioning/repository/github/mutator_test.go index a6bbbe22c11..5094ed07206 100644 --- a/pkg/registry/apis/provisioning/repository/github/mutator_test.go +++ b/pkg/registry/apis/provisioning/repository/github/mutator_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/registry/apis/provisioning/repository/github/repository.go b/pkg/registry/apis/provisioning/repository/github/repository.go index d2da1de41d1..ac95e54312f 100644 --- a/pkg/registry/apis/provisioning/repository/github/repository.go +++ b/pkg/registry/apis/provisioning/repository/github/repository.go @@ -10,7 +10,7 @@ import ( "github.com/grafana/grafana-app-sdk/logging" "k8s.io/apimachinery/pkg/util/validation/field" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/git" "github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath" diff --git a/pkg/registry/apis/provisioning/repository/github/repository_test.go b/pkg/registry/apis/provisioning/repository/github/repository_test.go index 962a7a4e2b1..3a11127e9da 100644 --- a/pkg/registry/apis/provisioning/repository/github/repository_test.go +++ b/pkg/registry/apis/provisioning/repository/github/repository_test.go @@ -14,7 +14,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" field "k8s.io/apimachinery/pkg/util/validation/field" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/git" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" diff --git a/pkg/registry/apis/provisioning/repository/local/local.go b/pkg/registry/apis/provisioning/repository/local/local.go index 0ab620e6639..98413cc1094 100644 --- a/pkg/registry/apis/provisioning/repository/local/local.go +++ b/pkg/registry/apis/provisioning/repository/local/local.go @@ -22,7 +22,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath" ) diff --git a/pkg/registry/apis/provisioning/repository/local/local_test.go b/pkg/registry/apis/provisioning/repository/local/local_test.go index 77a0b99fa09..378a3170478 100644 --- a/pkg/registry/apis/provisioning/repository/local/local_test.go +++ b/pkg/registry/apis/provisioning/repository/local/local_test.go @@ -18,7 +18,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" field "k8s.io/apimachinery/pkg/util/validation/field" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/repository/reader_mock.go b/pkg/registry/apis/provisioning/repository/reader_mock.go index dd6a73ab567..c7f56f5847c 100644 --- a/pkg/registry/apis/provisioning/repository/reader_mock.go +++ b/pkg/registry/apis/provisioning/repository/reader_mock.go @@ -8,7 +8,7 @@ import ( mock "github.com/stretchr/testify/mock" field "k8s.io/apimachinery/pkg/util/validation/field" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockReader is an autogenerated mock type for the Reader type diff --git a/pkg/registry/apis/provisioning/repository/repository.go b/pkg/registry/apis/provisioning/repository/repository.go index 718660c9fab..5608366060b 100644 --- a/pkg/registry/apis/provisioning/repository/repository.go +++ b/pkg/registry/apis/provisioning/repository/repository.go @@ -8,7 +8,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/validation/field" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // FIXME: the name of the mock is different because there is another generated mock for Repository diff --git a/pkg/registry/apis/provisioning/repository/repository_mock.go b/pkg/registry/apis/provisioning/repository/repository_mock.go index 05fc5a3832a..e78a7467884 100644 --- a/pkg/registry/apis/provisioning/repository/repository_mock.go +++ b/pkg/registry/apis/provisioning/repository/repository_mock.go @@ -10,7 +10,7 @@ import ( mock "github.com/stretchr/testify/mock" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockRepository is an autogenerated mock type for the Repository type diff --git a/pkg/registry/apis/provisioning/repository/staged.go b/pkg/registry/apis/provisioning/repository/staged.go index ca5b59c72c9..1861d66336c 100644 --- a/pkg/registry/apis/provisioning/repository/staged.go +++ b/pkg/registry/apis/provisioning/repository/staged.go @@ -7,9 +7,14 @@ import ( "time" "github.com/grafana/grafana-app-sdk/logging" - "github.com/grafana/nanogit" ) +// ErrNothingToPush indicates that there are no changes to push to the remote repository +var ErrNothingToPush = errors.New("nothing to push") + +// ErrNothingToCommit indicates that there are no changes to commit +var ErrNothingToCommit = errors.New("nothing to commit") + //go:generate mockery --name WrapWithStageFn --structname MockWrapWithStageFn --inpackage --filename mock_wrap_with_stage_fn.go --with-expecter type WrapWithStageFn func(ctx context.Context, repo Repository, stageOptions StageOptions, fn func(repo Repository, staged bool) error) error @@ -83,7 +88,7 @@ func WrapWithStageAndPushIfPossible( } if err = staged.Push(ctx); err != nil { - if errors.Is(err, nanogit.ErrNothingToPush) { + if errors.Is(err, ErrNothingToPush) || errors.Is(err, ErrNothingToCommit) { return nil // OK, already pushed } return fmt.Errorf("wrapped push error: %w", err) diff --git a/pkg/registry/apis/provisioning/repository/staged_repository_mock.go b/pkg/registry/apis/provisioning/repository/staged_repository_mock.go index 85bfb4f3012..bd5b8785326 100644 --- a/pkg/registry/apis/provisioning/repository/staged_repository_mock.go +++ b/pkg/registry/apis/provisioning/repository/staged_repository_mock.go @@ -8,7 +8,7 @@ import ( mock "github.com/stretchr/testify/mock" field "k8s.io/apimachinery/pkg/util/validation/field" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockStagedRepository is an autogenerated mock type for the StagedRepository type diff --git a/pkg/registry/apis/provisioning/repository/staged_test.go b/pkg/registry/apis/provisioning/repository/staged_test.go index 3efe8ebbd5e..c640e2cd0db 100644 --- a/pkg/registry/apis/provisioning/repository/staged_test.go +++ b/pkg/registry/apis/provisioning/repository/staged_test.go @@ -3,6 +3,7 @@ package repository import ( "context" "errors" + "fmt" "testing" "github.com/stretchr/testify/mock" @@ -127,6 +128,84 @@ func TestWrapWithStageAndPushIfPossible(t *testing.T) { return nil }, }, + { + name: "nothing to push - should not error", + setupMocks: func(t *testing.T) *mockStagedRepo { + mockRepo := NewMockStageableRepository(t) + mockStaged := NewMockStagedRepository(t) + + mockRepo.EXPECT().Stage(mock.Anything, StageOptions{}).Return(mockStaged, nil) + mockStaged.EXPECT().Push(mock.Anything).Return(ErrNothingToPush) + mockStaged.EXPECT().Remove(mock.Anything).Return(nil) + + return &mockStagedRepo{ + MockStageableRepository: mockRepo, + MockStagedRepository: mockStaged, + } + }, + operation: func(repo Repository, staged bool) error { + return nil + }, + }, + { + name: "nothing to commit - should not error", + setupMocks: func(t *testing.T) *mockStagedRepo { + mockRepo := NewMockStageableRepository(t) + mockStaged := NewMockStagedRepository(t) + + mockRepo.EXPECT().Stage(mock.Anything, StageOptions{}).Return(mockStaged, nil) + mockStaged.EXPECT().Push(mock.Anything).Return(ErrNothingToCommit) + mockStaged.EXPECT().Remove(mock.Anything).Return(nil) + + return &mockStagedRepo{ + MockStageableRepository: mockRepo, + MockStagedRepository: mockStaged, + } + }, + operation: func(repo Repository, staged bool) error { + return nil + }, + }, + { + name: "wrapped nothing to push error - should not error", + setupMocks: func(t *testing.T) *mockStagedRepo { + mockRepo := NewMockStageableRepository(t) + mockStaged := NewMockStagedRepository(t) + + wrappedErr := fmt.Errorf("some wrapper: %w", ErrNothingToPush) + mockRepo.EXPECT().Stage(mock.Anything, StageOptions{}).Return(mockStaged, nil) + mockStaged.EXPECT().Push(mock.Anything).Return(wrappedErr) + mockStaged.EXPECT().Remove(mock.Anything).Return(nil) + + return &mockStagedRepo{ + MockStageableRepository: mockRepo, + MockStagedRepository: mockStaged, + } + }, + operation: func(repo Repository, staged bool) error { + return nil + }, + }, + { + name: "wrapped nothing to commit error - should not error", + setupMocks: func(t *testing.T) *mockStagedRepo { + mockRepo := NewMockStageableRepository(t) + mockStaged := NewMockStagedRepository(t) + + wrappedErr := fmt.Errorf("some wrapper: %w", ErrNothingToCommit) + mockRepo.EXPECT().Stage(mock.Anything, StageOptions{}).Return(mockStaged, nil) + mockStaged.EXPECT().Push(mock.Anything).Return(wrappedErr) + mockStaged.EXPECT().Remove(mock.Anything).Return(nil) + + return &mockStagedRepo{ + MockStageableRepository: mockRepo, + MockStagedRepository: mockStaged, + } + }, + operation: func(repo Repository, staged bool) error { + return nil + }, + }, } for _, tt := range tests { diff --git a/pkg/registry/apis/provisioning/repository/test.go b/pkg/registry/apis/provisioning/repository/test.go index f8b72d5b2af..403ac24fa38 100644 --- a/pkg/registry/apis/provisioning/repository/test.go +++ b/pkg/registry/apis/provisioning/repository/test.go @@ -9,7 +9,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/validation/field" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // Tester is a struct that implements the Tester interface diff --git a/pkg/registry/apis/provisioning/repository/test_test.go b/pkg/registry/apis/provisioning/repository/test_test.go index 9168d529ea0..56d73e13a90 100644 --- a/pkg/registry/apis/provisioning/repository/test_test.go +++ b/pkg/registry/apis/provisioning/repository/test_test.go @@ -11,7 +11,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/validation/field" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) func TestValidateRepository(t *testing.T) { diff --git a/pkg/registry/apis/provisioning/repository/versioned_mock.go b/pkg/registry/apis/provisioning/repository/versioned_mock.go index dc711f10f30..36ef2fe9a96 100644 --- a/pkg/registry/apis/provisioning/repository/versioned_mock.go +++ b/pkg/registry/apis/provisioning/repository/versioned_mock.go @@ -5,7 +5,7 @@ package repository import ( context "context" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" mock "github.com/stretchr/testify/mock" ) diff --git a/pkg/registry/apis/provisioning/repository/workflows.go b/pkg/registry/apis/provisioning/repository/workflows.go index 9750bde0962..48ba1319a62 100644 --- a/pkg/registry/apis/provisioning/repository/workflows.go +++ b/pkg/registry/apis/provisioning/repository/workflows.go @@ -3,7 +3,7 @@ package repository import ( apierrors "k8s.io/apimachinery/pkg/api/errors" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) func IsWriteAllowed(repo *provisioning.Repository, ref string) error { diff --git a/pkg/registry/apis/provisioning/repository/workflows_test.go b/pkg/registry/apis/provisioning/repository/workflows_test.go index 6f2e337494d..995c85e0eb5 100644 --- a/pkg/registry/apis/provisioning/repository/workflows_test.go +++ b/pkg/registry/apis/provisioning/repository/workflows_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" apierrors "k8s.io/apimachinery/pkg/api/errors" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) func TestIsWriteAllowed(t *testing.T) { diff --git a/pkg/registry/apis/provisioning/resources/dualwriter.go b/pkg/registry/apis/provisioning/resources/dualwriter.go index 50efaa13048..78042c35a12 100644 --- a/pkg/registry/apis/provisioning/resources/dualwriter.go +++ b/pkg/registry/apis/provisioning/resources/dualwriter.go @@ -10,10 +10,10 @@ import ( authlib "github.com/grafana/authlib/types" "github.com/grafana/grafana-app-sdk/logging" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath" ) diff --git a/pkg/registry/apis/provisioning/resources/fileformat.go b/pkg/registry/apis/provisioning/resources/fileformat.go index 0ed0b287199..b35afd74781 100644 --- a/pkg/registry/apis/provisioning/resources/fileformat.go +++ b/pkg/registry/apis/provisioning/resources/fileformat.go @@ -14,7 +14,7 @@ import ( "github.com/grafana/grafana-app-sdk/logging" dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/resources/fileformat_test.go b/pkg/registry/apis/provisioning/resources/fileformat_test.go index 611578abb45..3013927edc9 100644 --- a/pkg/registry/apis/provisioning/resources/fileformat_test.go +++ b/pkg/registry/apis/provisioning/resources/fileformat_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime/schema" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/resources/id.go b/pkg/registry/apis/provisioning/resources/id.go index b32355b4b5f..2ffaa0cc5bc 100644 --- a/pkg/registry/apis/provisioning/resources/id.go +++ b/pkg/registry/apis/provisioning/resources/id.go @@ -5,7 +5,7 @@ import ( "encoding/base64" "strings" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath" ) diff --git a/pkg/registry/apis/provisioning/resources/object.go b/pkg/registry/apis/provisioning/resources/object.go index 9faa4412fce..a30694417bb 100644 --- a/pkg/registry/apis/provisioning/resources/object.go +++ b/pkg/registry/apis/provisioning/resources/object.go @@ -8,8 +8,8 @@ import ( dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy" "github.com/grafana/grafana/pkg/storage/legacysql/dualwrite" "github.com/grafana/grafana/pkg/storage/unified/resource" diff --git a/pkg/registry/apis/provisioning/resources/parser.go b/pkg/registry/apis/provisioning/resources/parser.go index 352e92fa27a..2b3f0c2deac 100644 --- a/pkg/registry/apis/provisioning/resources/parser.go +++ b/pkg/registry/apis/provisioning/resources/parser.go @@ -16,10 +16,10 @@ import ( "github.com/grafana/grafana-app-sdk/logging" dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath" "github.com/grafana/grafana/pkg/util" diff --git a/pkg/registry/apis/provisioning/resources/parser_test.go b/pkg/registry/apis/provisioning/resources/parser_test.go index d718445fc57..61f374a8e05 100644 --- a/pkg/registry/apis/provisioning/resources/parser_test.go +++ b/pkg/registry/apis/provisioning/resources/parser_test.go @@ -9,7 +9,7 @@ import ( dashboardV0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" dashboardV1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/resources/repository.go b/pkg/registry/apis/provisioning/resources/repository.go index d12747b7bc8..12747b9834d 100644 --- a/pkg/registry/apis/provisioning/resources/repository.go +++ b/pkg/registry/apis/provisioning/resources/repository.go @@ -9,8 +9,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath" ) diff --git a/pkg/registry/apis/provisioning/resources/repository_resources_mock.go b/pkg/registry/apis/provisioning/resources/repository_resources_mock.go index 9ff7171f68c..978d91f1d4d 100644 --- a/pkg/registry/apis/provisioning/resources/repository_resources_mock.go +++ b/pkg/registry/apis/provisioning/resources/repository_resources_mock.go @@ -10,7 +10,7 @@ import ( unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockRepositoryResources is an autogenerated mock type for the RepositoryResources type diff --git a/pkg/registry/apis/provisioning/resources/resource_lister_mock.go b/pkg/registry/apis/provisioning/resources/resource_lister_mock.go index 29fb1712f51..2bf441cd93d 100644 --- a/pkg/registry/apis/provisioning/resources/resource_lister_mock.go +++ b/pkg/registry/apis/provisioning/resources/resource_lister_mock.go @@ -5,7 +5,7 @@ package resources import ( context "context" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" mock "github.com/stretchr/testify/mock" ) diff --git a/pkg/registry/apis/provisioning/resources/resources.go b/pkg/registry/apis/provisioning/resources/resources.go index 28dd06ea43a..ec5942c21ec 100644 --- a/pkg/registry/apis/provisioning/resources/resources.go +++ b/pkg/registry/apis/provisioning/resources/resources.go @@ -92,19 +92,29 @@ func (r *ResourcesManager) WriteResourceFileFromObject(ctx context.Context, obj if title == "" { title = name } - folder := meta.GetFolder() + folder := meta.GetFolder() // Get the absolute path of the folder rootFolder := RootFolder(r.repo.Config()) - fid, ok := r.folders.Tree().DirPath(folder, rootFolder) - if !ok { - return "", fmt.Errorf("folder not found in tree: %s", folder) + + // If no folder is specified in the file, set it to the root to ensure everything is written under it + var fid Folder + if folder == "" { + fid = Folder{ID: rootFolder} + meta.SetFolder(rootFolder) // Set the folder in the metadata to the root folder + } else { + var ok bool + fid, ok = r.folders.Tree().DirPath(folder, rootFolder) + if !ok { + return "", fmt.Errorf("folder %s NOT found in tree with root: %s", folder, rootFolder) + } } fileName := slugify.Slugify(title) + ".json" if fid.Path != "" { fileName = safepath.Join(fid.Path, fileName) } + if options.Path != "" { fileName = safepath.Join(options.Path, fileName) } diff --git a/pkg/registry/apis/provisioning/resources/tree.go b/pkg/registry/apis/provisioning/resources/tree.go index 427384e8753..ce92f104b8b 100644 --- a/pkg/registry/apis/provisioning/resources/tree.go +++ b/pkg/registry/apis/provisioning/resources/tree.go @@ -7,8 +7,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath" ) diff --git a/pkg/registry/apis/provisioning/routes.go b/pkg/registry/apis/provisioning/routes.go index 5e222882bd0..749c4a8d2ed 100644 --- a/pkg/registry/apis/provisioning/routes.go +++ b/pkg/registry/apis/provisioning/routes.go @@ -12,7 +12,7 @@ import ( "k8s.io/kube-openapi/pkg/validation/spec" authlib "github.com/grafana/authlib/types" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/services/apiserver/builder" "github.com/grafana/grafana/pkg/storage/legacysql/dualwrite" "github.com/grafana/grafana/pkg/util/errhttp" diff --git a/pkg/registry/apis/provisioning/secrets/mocks/decrypt_service_mock.go b/pkg/registry/apis/provisioning/secrets/mocks/decrypt_service_mock.go index b5cca7ebe62..967e77bf216 100644 --- a/pkg/registry/apis/provisioning/secrets/mocks/decrypt_service_mock.go +++ b/pkg/registry/apis/provisioning/secrets/mocks/decrypt_service_mock.go @@ -67,16 +67,9 @@ func (_c *MockDecryptService_Close_Call) RunAndReturn(run func() error) *MockDec return _c } -// Decrypt provides a mock function with given fields: ctx, namespace, names -func (_m *MockDecryptService) Decrypt(ctx context.Context, namespace string, names ...string) (map[string]contracts.DecryptResult, error) { - _va := make([]interface{}, len(names)) - for _i := range names { - _va[_i] = names[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, namespace) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) +// Decrypt provides a mock function with given fields: ctx, serviceName, namespace, names +func (_m *MockDecryptService) Decrypt(ctx context.Context, serviceName string, namespace string, names []string) (map[string]contracts.DecryptResult, error) { + ret := _m.Called(ctx, serviceName, namespace, names) if len(ret) == 0 { panic("no return value specified for Decrypt") @@ -84,19 +77,19 @@ func (_m *MockDecryptService) Decrypt(ctx context.Context, namespace string, nam var r0 map[string]contracts.DecryptResult var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, ...string) (map[string]contracts.DecryptResult, error)); ok { - return rf(ctx, namespace, names...) + if rf, ok := ret.Get(0).(func(context.Context, string, string, []string) (map[string]contracts.DecryptResult, error)); ok { + return rf(ctx, serviceName, namespace, names) } - if rf, ok := ret.Get(0).(func(context.Context, string, ...string) map[string]contracts.DecryptResult); ok { - r0 = rf(ctx, namespace, names...) + if rf, ok := ret.Get(0).(func(context.Context, string, string, []string) map[string]contracts.DecryptResult); ok { + r0 = rf(ctx, serviceName, namespace, names) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(map[string]contracts.DecryptResult) } } - if rf, ok := ret.Get(1).(func(context.Context, string, ...string) error); ok { - r1 = rf(ctx, namespace, names...) + if rf, ok := ret.Get(1).(func(context.Context, string, string, []string) error); ok { + r1 = rf(ctx, serviceName, namespace, names) } else { r1 = ret.Error(1) } @@ -111,22 +104,16 @@ type MockDecryptService_Decrypt_Call struct { // Decrypt is a helper method to define mock.On call // - ctx context.Context +// - serviceName string // - namespace string -// - names ...string -func (_e *MockDecryptService_Expecter) Decrypt(ctx interface{}, namespace interface{}, names ...interface{}) *MockDecryptService_Decrypt_Call { - return &MockDecryptService_Decrypt_Call{Call: _e.mock.On("Decrypt", - append([]interface{}{ctx, namespace}, names...)...)} +// - names []string +func (_e *MockDecryptService_Expecter) Decrypt(ctx interface{}, serviceName interface{}, namespace interface{}, names interface{}) *MockDecryptService_Decrypt_Call { + return &MockDecryptService_Decrypt_Call{Call: _e.mock.On("Decrypt", ctx, serviceName, namespace, names)} } -func (_c *MockDecryptService_Decrypt_Call) Run(run func(ctx context.Context, namespace string, names ...string)) *MockDecryptService_Decrypt_Call { +func (_c *MockDecryptService_Decrypt_Call) Run(run func(ctx context.Context, serviceName string, namespace string, names []string)) *MockDecryptService_Decrypt_Call { _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]string, len(args)-2) - for i, a := range args[2:] { - if a != nil { - variadicArgs[i] = a.(string) - } - } - run(args[0].(context.Context), args[1].(string), variadicArgs...) + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].([]string)) }) return _c } @@ -136,7 +123,7 @@ func (_c *MockDecryptService_Decrypt_Call) Return(_a0 map[string]contracts.Decry return _c } -func (_c *MockDecryptService_Decrypt_Call) RunAndReturn(run func(context.Context, string, ...string) (map[string]contracts.DecryptResult, error)) *MockDecryptService_Decrypt_Call { +func (_c *MockDecryptService_Decrypt_Call) RunAndReturn(run func(context.Context, string, string, []string) (map[string]contracts.DecryptResult, error)) *MockDecryptService_Decrypt_Call { _c.Call.Return(run) return _c } diff --git a/pkg/registry/apis/provisioning/secrets/repository.go b/pkg/registry/apis/provisioning/secrets/repository.go index 29a470dd993..09ba78e1389 100644 --- a/pkg/registry/apis/provisioning/secrets/repository.go +++ b/pkg/registry/apis/provisioning/secrets/repository.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/grafana/grafana-app-sdk/logging" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/secret" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/services/featuremgmt" diff --git a/pkg/registry/apis/provisioning/secrets/repository_secrets_mock.go b/pkg/registry/apis/provisioning/secrets/repository_secrets_mock.go index 763422ef7c5..7108be570f2 100644 --- a/pkg/registry/apis/provisioning/secrets/repository_secrets_mock.go +++ b/pkg/registry/apis/provisioning/secrets/repository_secrets_mock.go @@ -5,7 +5,7 @@ package secrets import ( context "context" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" mock "github.com/stretchr/testify/mock" ) diff --git a/pkg/registry/apis/provisioning/secrets/repository_test.go b/pkg/registry/apis/provisioning/secrets/repository_test.go index 028efd10853..bd53089fc75 100644 --- a/pkg/registry/apis/provisioning/secrets/repository_test.go +++ b/pkg/registry/apis/provisioning/secrets/repository_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/stretchr/testify/assert" diff --git a/pkg/registry/apis/provisioning/secrets/secret.go b/pkg/registry/apis/provisioning/secrets/secret.go index 9127e7b7868..f0c69e88153 100644 --- a/pkg/registry/apis/provisioning/secrets/secret.go +++ b/pkg/registry/apis/provisioning/secrets/secret.go @@ -4,8 +4,6 @@ import ( "context" "errors" - "github.com/grafana/authlib/types" - "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/registry/apis/secret" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/setting" @@ -109,13 +107,7 @@ func (s *secretsService) Encrypt(ctx context.Context, namespace, name string, da } func (s *secretsService) Decrypt(ctx context.Context, namespace string, name string) ([]byte, error) { - ns, err := types.ParseNamespace(namespace) - if err != nil { - return nil, err - } - ctx = identity.WithServiceIdentityContext(ctx, ns.OrgID, identity.WithServiceIdentityName(svcName)) - - results, err := s.decryptSvc.Decrypt(ctx, namespace, name) + results, err := s.decryptSvc.Decrypt(ctx, svcName, namespace, []string{name}) if err != nil { return nil, err } @@ -132,12 +124,6 @@ func (s *secretsService) Decrypt(ctx context.Context, namespace string, name str } func (s *secretsService) Delete(ctx context.Context, namespace string, name string) error { - ns, err := types.ParseNamespace(namespace) - if err != nil { - return err - } - - ctx = identity.WithServiceIdentityContext(ctx, ns.OrgID, identity.WithServiceIdentityName(svcName)) client, err := s.secureValues.Client(ctx, namespace) if err != nil { return err diff --git a/pkg/registry/apis/provisioning/secrets/secret_test.go b/pkg/registry/apis/provisioning/secrets/secret_test.go index e393ac7d8dd..d7097975f43 100644 --- a/pkg/registry/apis/provisioning/secrets/secret_test.go +++ b/pkg/registry/apis/provisioning/secrets/secret_test.go @@ -266,8 +266,9 @@ func TestSecretsService_Decrypt(t *testing.T) { // Verify that the context is not nil (the service creates a new StaticRequester) return ctx != nil }), + svcName, "test-namespace", - "test-secret", + []string{"test-secret"}, ).Return(map[string]secret.DecryptResult{ "test-secret": mockResult, }, nil) @@ -283,8 +284,9 @@ func TestSecretsService_Decrypt(t *testing.T) { mock.MatchedBy(func(ctx context.Context) bool { return ctx != nil }), + svcName, "test-namespace", - "test-secret", + []string{"test-secret"}, ).Return(nil, errors.New("decrypt service error")) }, expectedError: "decrypt service error", @@ -298,8 +300,9 @@ func TestSecretsService_Decrypt(t *testing.T) { mock.MatchedBy(func(ctx context.Context) bool { return ctx != nil }), + svcName, "test-namespace", - "test-secret", + []string{"test-secret"}, ).Return(map[string]secret.DecryptResult{}, nil) }, expectedError: secret.ErrDecryptNotFound.Error(), @@ -315,8 +318,9 @@ func TestSecretsService_Decrypt(t *testing.T) { mock.MatchedBy(func(ctx context.Context) bool { return ctx != nil }), + svcName, "test-namespace", - "test-secret", + []string{"test-secret"}, ).Return(map[string]secret.DecryptResult{ "test-secret": mockResult, }, nil) @@ -363,8 +367,9 @@ func TestSecretsService_Decrypt_ServiceIdentityContext(t *testing.T) { // At minimum, verify the context is not nil and is different from the original return ctx != nil }), + svcName, "test-namespace", - "test-secret", + []string{"test-secret"}, ).Return(map[string]secret.DecryptResult{ "test-secret": mockResult, }, nil) diff --git a/pkg/registry/apis/provisioning/test.go b/pkg/registry/apis/provisioning/test.go index 9813c37a0ad..9abb1d38a7f 100644 --- a/pkg/registry/apis/provisioning/test.go +++ b/pkg/registry/apis/provisioning/test.go @@ -13,8 +13,8 @@ import ( "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - client "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + client "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/types.go b/pkg/registry/apis/provisioning/types.go index d804643cd21..8c030b652e1 100644 --- a/pkg/registry/apis/provisioning/types.go +++ b/pkg/registry/apis/provisioning/types.go @@ -3,8 +3,8 @@ package provisioning import ( "context" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" - client "github.com/grafana/grafana/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + client "github.com/grafana/grafana/apps/provisioning/pkg/generated/clientset/versioned/typed/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/usage/usage.go b/pkg/registry/apis/provisioning/usage/usage.go index 485c9fe19e4..b9a1ed938c9 100644 --- a/pkg/registry/apis/provisioning/usage/usage.go +++ b/pkg/registry/apis/provisioning/usage/usage.go @@ -9,8 +9,8 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apiserver/pkg/endpoints/request" + listers "github.com/grafana/grafana/apps/provisioning/pkg/generated/listers/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" - listers "github.com/grafana/grafana/pkg/generated/listers/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/storage/unified/resource" diff --git a/pkg/registry/apis/provisioning/webhooks/mutator.go b/pkg/registry/apis/provisioning/webhooks/mutator.go index 10ad8288503..b93677228b0 100644 --- a/pkg/registry/apis/provisioning/webhooks/mutator.go +++ b/pkg/registry/apis/provisioning/webhooks/mutator.go @@ -3,7 +3,7 @@ package webhooks import ( "context" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/controller" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" "k8s.io/apimachinery/pkg/runtime" diff --git a/pkg/registry/apis/provisioning/webhooks/mutator_test.go b/pkg/registry/apis/provisioning/webhooks/mutator_test.go index 07581398fdc..209d95d5284 100644 --- a/pkg/registry/apis/provisioning/webhooks/mutator_test.go +++ b/pkg/registry/apis/provisioning/webhooks/mutator_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/changes.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/changes.go index 4bd6d5bf844..12bc6815085 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/changes.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/changes.go @@ -9,7 +9,7 @@ import ( "github.com/grafana/grafana-app-sdk/logging" dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" "github.com/grafana/grafana/pkg/infra/slugify" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/changes_test.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/changes_test.go index d31198eb595..6692d6248f3 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/changes_test.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/changes_test.go @@ -13,8 +13,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/comment_test.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/comment_test.go index fdd8e3f6dd4..e7f1d686f9f 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/comment_test.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/comment_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime/schema" - "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" ) diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/mock_evaluator.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/mock_evaluator.go index 9d328127af4..e0f57d8ce0d 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/mock_evaluator.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/mock_evaluator.go @@ -10,7 +10,7 @@ import ( repository "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockEvaluator is an autogenerated mock type for the Evaluator type diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/mock_pullrequest_repo.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/mock_pullrequest_repo.go index f8c4b9ba5a9..6803341ecbc 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/mock_pullrequest_repo.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/mock_pullrequest_repo.go @@ -8,7 +8,7 @@ import ( repository "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" mock "github.com/stretchr/testify/mock" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockPullRequestRepo is an autogenerated mock type for the PullRequestRepo type diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/render.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/render.go index 897f73a2e74..a6de6435c7a 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/render.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/render.go @@ -10,8 +10,8 @@ import ( "strings" "time" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/rendering" "github.com/grafana/grafana/pkg/storage/unified/resourcepb" diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/render_mock.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/render_mock.go index e2a4ed6db94..3c099d6e543 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/render_mock.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/render_mock.go @@ -8,7 +8,7 @@ import ( mock "github.com/stretchr/testify/mock" - v0alpha1 "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + v0alpha1 "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" ) // MockScreenshotRenderer is an autogenerated mock type for the ScreenshotRenderer type diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/render_test.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/render_test.go index 3cb2ac1c3c8..a54d12af1b6 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/render_test.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/render_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/rendering" "github.com/grafana/grafana/pkg/storage/unified/resourcepb" diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/worker.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/worker.go index b52c4854b2a..1d838faa0d4 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/worker.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/worker.go @@ -8,7 +8,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "github.com/grafana/grafana-app-sdk/logging" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/resources" diff --git a/pkg/registry/apis/provisioning/webhooks/pullrequest/worker_test.go b/pkg/registry/apis/provisioning/webhooks/pullrequest/worker_test.go index 4ba1cae7cb3..858a3675bfa 100644 --- a/pkg/registry/apis/provisioning/webhooks/pullrequest/worker_test.go +++ b/pkg/registry/apis/provisioning/webhooks/pullrequest/worker_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" ) diff --git a/pkg/registry/apis/provisioning/webhooks/register.go b/pkg/registry/apis/provisioning/webhooks/register.go index 0452030f27f..7de867fed9a 100644 --- a/pkg/registry/apis/provisioning/webhooks/register.go +++ b/pkg/registry/apis/provisioning/webhooks/register.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/grafana/grafana-app-sdk/logging" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" provisioningapis "github.com/grafana/grafana/pkg/registry/apis/provisioning" "github.com/grafana/grafana/pkg/registry/apis/provisioning/controller" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" diff --git a/pkg/registry/apis/provisioning/webhooks/render.go b/pkg/registry/apis/provisioning/webhooks/render.go index 06a19923a8c..77cda32198e 100644 --- a/pkg/registry/apis/provisioning/webhooks/render.go +++ b/pkg/registry/apis/provisioning/webhooks/render.go @@ -15,7 +15,7 @@ import ( "k8s.io/apiserver/pkg/registry/rest" "k8s.io/kube-openapi/pkg/spec3" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" provisioningapis "github.com/grafana/grafana/pkg/registry/apis/provisioning" "github.com/grafana/grafana/pkg/storage/unified/resource" diff --git a/pkg/registry/apis/provisioning/webhooks/repository.go b/pkg/registry/apis/provisioning/webhooks/repository.go index 4e08111dec5..c122bdba2f4 100644 --- a/pkg/registry/apis/provisioning/webhooks/repository.go +++ b/pkg/registry/apis/provisioning/webhooks/repository.go @@ -11,7 +11,7 @@ import ( "github.com/google/go-github/v70/github" "github.com/google/uuid" "github.com/grafana/grafana-app-sdk/logging" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" pgh "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/github" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" diff --git a/pkg/registry/apis/provisioning/webhooks/repository_test.go b/pkg/registry/apis/provisioning/webhooks/repository_test.go index 76de9f50f02..38b561fe0eb 100644 --- a/pkg/registry/apis/provisioning/webhooks/repository_test.go +++ b/pkg/registry/apis/provisioning/webhooks/repository_test.go @@ -14,7 +14,7 @@ import ( "strings" "testing" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository/github" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" "github.com/stretchr/testify/mock" diff --git a/pkg/registry/apis/provisioning/webhooks/webhook.go b/pkg/registry/apis/provisioning/webhooks/webhook.go index 4c1ef54ddfe..bf239f31e24 100644 --- a/pkg/registry/apis/provisioning/webhooks/webhook.go +++ b/pkg/registry/apis/provisioning/webhooks/webhook.go @@ -14,8 +14,8 @@ import ( "k8s.io/kube-openapi/pkg/spec3" "github.com/grafana/grafana-app-sdk/logging" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" provisioningapis "github.com/grafana/grafana/pkg/registry/apis/provisioning" "github.com/grafana/grafana/pkg/registry/apis/provisioning/repository" "github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks/pullrequest" diff --git a/pkg/registry/apis/query/client/instance_provider.go b/pkg/registry/apis/query/client/instance_provider.go new file mode 100644 index 00000000000..8716ae5a41c --- /dev/null +++ b/pkg/registry/apis/query/client/instance_provider.go @@ -0,0 +1,61 @@ +package client + +import ( + "context" + + data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/registry/apis/query/clientapi" + "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" + "github.com/grafana/grafana/pkg/setting" +) + +type singleTenantInstanceProvider struct { + client clientapi.QueryDataClient + features featuremgmt.FeatureToggles + cfg *setting.Cfg +} + +type singleTenantInstance struct { + client clientapi.QueryDataClient + features featuremgmt.FeatureToggles + cfg *setting.Cfg +} + +func (t *singleTenantInstance) GetDataSourceClient(_ context.Context, _ data.DataSourceRef, _ map[string]string) (clientapi.QueryDataClient, error) { + return t.client, nil +} + +func NewSingleTenantInstanceProvider(cfg *setting.Cfg, features featuremgmt.FeatureToggles, p plugins.Client, ctxProv *plugincontext.Provider, accessControl accesscontrol.AccessControl) clientapi.InstanceProvider { + return &singleTenantInstanceProvider{ + cfg: cfg, + features: features, + client: newQueryClientForPluginClient(p, ctxProv, accessControl), + } +} + +func (s *singleTenantInstanceProvider) GetInstance(_ context.Context) (clientapi.Instance, error) { + return &singleTenantInstance{ + client: s.client, + features: s.features, + cfg: s.cfg, + }, nil +} + +func (s *singleTenantInstance) GetSettings() clientapi.InstanceConfigurationSettings { + return clientapi.InstanceConfigurationSettings{ + FeatureToggles: s.features, + SQLExpressionCellLimit: s.cfg.SQLExpressionCellLimit, + SQLExpressionOutputCellLimit: s.cfg.SQLExpressionOutputCellLimit, + SQLExpressionTimeout: s.cfg.SQLExpressionTimeout, + ExpressionsEnabled: s.cfg.ExpressionsEnabled, + } +} + +func (s *singleTenantInstance) GetLogger(parent log.Logger) log.Logger { + // currently we do not add any extra info + return parent.New() +} diff --git a/pkg/registry/apis/query/client/supplier.go b/pkg/registry/apis/query/client/supplier.go deleted file mode 100644 index 08c462d466c..00000000000 --- a/pkg/registry/apis/query/client/supplier.go +++ /dev/null @@ -1,44 +0,0 @@ -package client - -import ( - "context" - - data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" - "github.com/grafana/grafana/pkg/plugins" - "github.com/grafana/grafana/pkg/registry/apis/query/clientapi" - "github.com/grafana/grafana/pkg/services/accesscontrol" - "github.com/grafana/grafana/pkg/services/featuremgmt" - "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" - "github.com/grafana/grafana/pkg/setting" -) - -type singleTenantClientSupplier struct { - client clientapi.QueryDataClient - features featuremgmt.FeatureToggles - cfg *setting.Cfg -} - -func NewSingleTenantClientSupplier(cfg *setting.Cfg, features featuremgmt.FeatureToggles, p plugins.Client, ctxProv *plugincontext.Provider, accessControl accesscontrol.AccessControl) clientapi.DataSourceClientSupplier { - return &singleTenantClientSupplier{ - cfg: cfg, - features: features, - client: newQueryClientForPluginClient(p, ctxProv, accessControl), - } -} - -func (s *singleTenantClientSupplier) GetDataSourceClient(_ context.Context, _ data.DataSourceRef, _ map[string]string, _ clientapi.InstanceConfigurationSettings) (clientapi.QueryDataClient, error) { - return s.client, nil -} - -func (s *singleTenantClientSupplier) GetInstanceConfigurationSettings(ctx context.Context) (clientapi.InstanceConfigurationSettings, error) { - return clientapi.InstanceConfigurationSettings{ - StackID: 0, - FeatureToggles: s.features, - FullConfig: nil, - Options: nil, - SQLExpressionCellLimit: s.cfg.SQLExpressionCellLimit, - SQLExpressionOutputCellLimit: s.cfg.SQLExpressionOutputCellLimit, - SQLExpressionTimeout: s.cfg.SQLExpressionTimeout, - ExpressionsEnabled: s.cfg.ExpressionsEnabled, - }, nil -} diff --git a/pkg/registry/apis/query/clientapi/clientapi.go b/pkg/registry/apis/query/clientapi/clientapi.go index 7e83565dbea..46f9167199b 100644 --- a/pkg/registry/apis/query/clientapi/clientapi.go +++ b/pkg/registry/apis/query/clientapi/clientapi.go @@ -7,6 +7,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" + "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/featuremgmt" ) @@ -20,19 +21,20 @@ type QueryDataClient interface { } type InstanceConfigurationSettings struct { - StackID uint32 FeatureToggles featuremgmt.FeatureToggles - FullConfig map[string]map[string]string // configuration file settings - Options map[string]string // additional settings related to an instance as set by grafana SQLExpressionCellLimit int64 SQLExpressionOutputCellLimit int64 SQLExpressionTimeout time.Duration ExpressionsEnabled bool } -type DataSourceClientSupplier interface { - // Get a client for a given datasource - GetDataSourceClient(ctx context.Context, ref data.DataSourceRef, headers map[string]string, instanceConfig InstanceConfigurationSettings) (QueryDataClient, error) +type Instance interface { + GetDataSourceClient(ctx context.Context, ref data.DataSourceRef, headers map[string]string) (QueryDataClient, error) // fetch information on the grafana instance (e.g. feature toggles) - GetInstanceConfigurationSettings(ctx context.Context) (InstanceConfigurationSettings, error) + GetSettings() InstanceConfigurationSettings + GetLogger(parent log.Logger) log.Logger +} + +type InstanceProvider interface { + GetInstance(ctx context.Context) (Instance, error) } diff --git a/pkg/registry/apis/query/query.go b/pkg/registry/apis/query/query.go index a70aa657765..ae202728a0d 100644 --- a/pkg/registry/apis/query/query.go +++ b/pkg/registry/apis/query/query.go @@ -246,20 +246,21 @@ func handleQuery(ctx context.Context, raw query.QueryDataRequest, b QueryAPIBuil headers := ExtractKnownHeaders(httpreq.Header) - instanceConfig, err := b.clientSupplier.GetInstanceConfigurationSettings(ctx) + instance, err := b.instanceProvider.GetInstance(ctx) if err != nil { connectLogger.Error("failed to get instance configuration settings", "err", err) responder.Error(err) return nil, err } - dsQuerierLoggerWithSlug := connectLogger.New("slug", instanceConfig.Options["slug"], "ruleuid", headers["X-Rule-Uid"]) + instanceConfig := instance.GetSettings() - mtDsClientBuilder := mtdsclient.NewMtDatasourceClientBuilderWithClientSupplier( - b.clientSupplier, + dsQuerierLoggerWithSlug := instance.GetLogger(connectLogger).New("ruleuid", headers["X-Rule-Uid"]) + + mtDsClientBuilder := mtdsclient.NewMtDatasourceClientBuilderWithInstance( + instance, ctx, headers, - instanceConfig, dsQuerierLoggerWithSlug, ) diff --git a/pkg/registry/apis/query/query_test.go b/pkg/registry/apis/query/query_test.go index 530e3a5d6b6..cd74c0dca0f 100644 --- a/pkg/registry/apis/query/query_test.go +++ b/pkg/registry/apis/query/query_test.go @@ -159,7 +159,7 @@ func TestQueryAPI(t *testing.T) { Features: featuremgmt.WithFeatures(featuremgmt.FlagSqlExpressions), Tracer: tracing.InitializeTracerForTest(), }, - clientSupplier: mockClient{ + instanceProvider: mockClient{ stubbedFrame: tc.stubbedFrame, }, tracer: tracing.InitializeTracerForTest(), @@ -250,7 +250,18 @@ type mockClient struct { stubbedFrame *data.Frame } -func (m mockClient) GetDataSourceClient(ctx context.Context, ref dataapi.DataSourceRef, headers map[string]string, instanceConfig clientapi.InstanceConfigurationSettings) (clientapi.QueryDataClient, error) { +func (m mockClient) GetInstance(ctx context.Context) (clientapi.Instance, error) { + mclient := mockClient{ + stubbedFrame: m.stubbedFrame, + } + return mclient, nil +} + +func (m mockClient) GetLogger(parent log.Logger) log.Logger { + return parent.New() +} + +func (m mockClient) GetDataSourceClient(ctx context.Context, ref dataapi.DataSourceRef, headers map[string]string) (clientapi.QueryDataClient, error) { mclient := mockClient{ stubbedFrame: m.stubbedFrame, } @@ -281,11 +292,11 @@ func (m mockClient) CheckHealth(ctx context.Context, req *backend.CheckHealthReq return nil, nil } -func (m mockClient) GetInstanceConfigurationSettings(ctx context.Context) (clientapi.InstanceConfigurationSettings, error) { +func (m mockClient) GetSettings() clientapi.InstanceConfigurationSettings { return clientapi.InstanceConfigurationSettings{ ExpressionsEnabled: true, FeatureToggles: featuremgmt.WithFeatures(featuremgmt.FlagSqlExpressions), - }, nil + } } type mockLegacyDataSourceLookup struct{} diff --git a/pkg/registry/apis/query/register.go b/pkg/registry/apis/query/register.go index 18a7d2b2a9d..7611c70aff1 100644 --- a/pkg/registry/apis/query/register.go +++ b/pkg/registry/apis/query/register.go @@ -45,7 +45,7 @@ type QueryAPIBuilder struct { tracer tracing.Tracer metrics *metrics.ExprMetrics - clientSupplier clientapi.DataSourceClientSupplier + instanceProvider clientapi.InstanceProvider registry query.DataSourceApiServerRegistry converter *expr.ResultConverter queryTypes *query.QueryTypeDefinitionList @@ -55,7 +55,7 @@ type QueryAPIBuilder struct { func NewQueryAPIBuilder( features featuremgmt.FeatureToggles, - clientSupplier clientapi.DataSourceClientSupplier, + instanceProvider clientapi.InstanceProvider, ar authorizer.Authorizer, registry query.DataSourceApiServerRegistry, registerer prometheus.Registerer, @@ -81,7 +81,7 @@ func NewQueryAPIBuilder( return &QueryAPIBuilder{ concurrentQueryLimit: 4, log: log.New("query_apiserver"), - clientSupplier: clientSupplier, + instanceProvider: instanceProvider, authorizer: ar, registry: registry, metrics: metrics.NewQueryServiceExpressionsMetrics(registerer), @@ -134,7 +134,7 @@ func RegisterAPIService( builder, err := NewQueryAPIBuilder( features, - client.NewSingleTenantClientSupplier(cfg, features, pluginClient, pCtxProvider, accessControl), + client.NewSingleTenantInstanceProvider(cfg, features, pluginClient, pCtxProvider, accessControl), ar, client.NewDataSourceRegistryFromStore(pluginStore, dataSourcesService), registerer, diff --git a/pkg/registry/apis/secret/contracts/decrypt.go b/pkg/registry/apis/secret/contracts/decrypt.go index 2c5403c790d..d0bf52ae65a 100644 --- a/pkg/registry/apis/secret/contracts/decrypt.go +++ b/pkg/registry/apis/secret/contracts/decrypt.go @@ -29,7 +29,7 @@ type DecryptAuthorizer interface { // DecryptService is the interface for the decrypt service. type DecryptService interface { - Decrypt(ctx context.Context, namespace string, names ...string) (map[string]DecryptResult, error) + Decrypt(ctx context.Context, serviceName string, namespace string, names []string) (map[string]DecryptResult, error) Close() error } diff --git a/pkg/registry/apis/secret/contracts/inline.go b/pkg/registry/apis/secret/contracts/inline.go index 4f677fa5c79..f2f3de71568 100644 --- a/pkg/registry/apis/secret/contracts/inline.go +++ b/pkg/registry/apis/secret/contracts/inline.go @@ -7,8 +7,8 @@ import ( ) type InlineSecureValueSupport interface { - // Check that the request user can reference a secret in the context of a given resource (owner) - CanReference(ctx context.Context, owner common.ObjectReference, values common.InlineSecureValues) error + // Check that the request user can reference secure value names in the context of a given resource (owner) + CanReference(ctx context.Context, owner common.ObjectReference, names ...string) error // CreateInline creates a secret that is owned by the referenced object // returns the name of the created secret or an error diff --git a/pkg/registry/apis/secret/decrypt/grpc_client.go b/pkg/registry/apis/secret/decrypt/grpc_client.go index 9851eec4526..2a7c5c606e7 100644 --- a/pkg/registry/apis/secret/decrypt/grpc_client.go +++ b/pkg/registry/apis/secret/decrypt/grpc_client.go @@ -10,7 +10,7 @@ import ( "github.com/fullstorydev/grpchan" authnlib "github.com/grafana/authlib/authn" - claims "github.com/grafana/authlib/types" + "github.com/grafana/authlib/types" "go.opentelemetry.io/otel/trace" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -23,8 +23,9 @@ import ( ) type GRPCDecryptClient struct { - conn *grpc.ClientConn - client decryptv1beta1.SecureValueDecrypterClient + conn *grpc.ClientConn + tracer trace.Tracer + tokenExchanger authnlib.TokenExchanger } var _ contracts.DecryptService = &GRPCDecryptClient{} @@ -38,14 +39,13 @@ type TLSConfig struct { InsecureSkipVerify bool } -func NewGRPCDecryptClient(tokenExchanger authnlib.TokenExchanger, tracer trace.Tracer, namespace, address string) (*GRPCDecryptClient, error) { - return NewGRPCDecryptClientWithTLS(tokenExchanger, tracer, namespace, address, TLSConfig{}) +func NewGRPCDecryptClient(tokenExchanger authnlib.TokenExchanger, tracer trace.Tracer, address string) (*GRPCDecryptClient, error) { + return NewGRPCDecryptClientWithTLS(tokenExchanger, tracer, address, TLSConfig{}) } func NewGRPCDecryptClientWithTLS( tokenExchanger authnlib.TokenExchanger, tracer trace.Tracer, - namespace string, address string, tlsConfig TLSConfig, ) (*GRPCDecryptClient, error) { @@ -66,22 +66,10 @@ func NewGRPCDecryptClientWithTLS( return nil, fmt.Errorf("failed to connect to grpc decrypt server at %s: %w", address, err) } - tokenExchangerInterceptor := authnlib.NewGrpcClientInterceptor( - tokenExchanger, - authnlib.WithClientInterceptorTracer(tracer), - authnlib.WithClientInterceptorNamespace(namespace), - authnlib.WithClientInterceptorAudience([]string{secretv1beta1.APIGroup}), - ) - - clientConn := grpchan.InterceptClientConn( - conn, - tokenExchangerInterceptor.UnaryClientInterceptor, - tokenExchangerInterceptor.StreamClientInterceptor, - ) - return &GRPCDecryptClient{ - conn: conn, - client: decryptv1beta1.NewSecureValueDecrypterClient(clientConn), + conn: conn, + tracer: tracer, + tokenExchanger: tokenExchanger, }, nil } @@ -120,6 +108,7 @@ func createTLSCredentials(config TLSConfig) (credentials.TransportCredentials, e return credentials.NewTLS(tlsConfig), nil } +// Close will close the underlying gRPC connection. After it is closed, the client cannot be used anymore. func (g *GRPCDecryptClient) Close() error { if g.conn != nil { return g.conn.Close() @@ -127,24 +116,27 @@ func (g *GRPCDecryptClient) Close() error { return nil } -func (g *GRPCDecryptClient) Decrypt(ctx context.Context, namespace string, names ...string) (map[string]contracts.DecryptResult, error) { - authInfo, ok := claims.AuthInfoFrom(ctx) - if !ok { - return nil, errors.New("missing auth info in context") +// Decrypt a set of secure value names in a given namespace for a specific service name. +func (g *GRPCDecryptClient) Decrypt(ctx context.Context, serviceName string, namespace string, names []string) (map[string]contracts.DecryptResult, error) { + _, err := types.ParseNamespace(namespace) + if err != nil { + return nil, err } - // Up until here the identity is the one set by the internal service, but when the request goes out to the gRPC server, - // the aggregator will use the access token which contains a different service identity for grafana as a whole. - // This is used for logging purposes only. - serviceIdentityList, ok := authInfo.GetExtra()[authnlib.ServiceIdentityKey] - if !ok || len(serviceIdentityList) != 1 { - return nil, errors.New("invalid service identity in auth info") - } + tokenExchangerInterceptor := authnlib.NewGrpcClientInterceptor( + g.tokenExchanger, + authnlib.WithClientInterceptorTracer(g.tracer), + authnlib.WithClientInterceptorNamespace(namespace), + authnlib.WithClientInterceptorAudience([]string{secretv1beta1.APIGroup}), + ) - serviceIdentity := serviceIdentityList[0] - if len(serviceIdentity) == 0 { - return nil, errors.New("empty service identity in auth info") - } + clientConn := grpchan.InterceptClientConn( + g.conn, + tokenExchangerInterceptor.UnaryClientInterceptor, + tokenExchangerInterceptor.StreamClientInterceptor, + ) + + client := decryptv1beta1.NewSecureValueDecrypterClient(clientConn) req := &decryptv1beta1.SecureValueDecryptRequest{ Namespace: namespace, @@ -154,11 +146,11 @@ func (g *GRPCDecryptClient) Decrypt(ctx context.Context, namespace string, names // Decryption will still use the service identity from the auth token, // but we also pass the service identity from the request metadata for auditing purposes. md := metadata.New(map[string]string{ - contracts.HeaderGrafanaServiceIdentityName: serviceIdentity, + contracts.HeaderGrafanaServiceIdentityName: serviceName, }) ctx = metadata.NewOutgoingContext(ctx, md) - resp, err := g.client.DecryptSecureValues(ctx, req) + resp, err := client.DecryptSecureValues(ctx, req) if err != nil { return nil, fmt.Errorf("grpc decrypt failed: %w", err) } diff --git a/pkg/registry/apis/secret/decrypt/local_client.go b/pkg/registry/apis/secret/decrypt/local_client.go index 6aa3f89ebe6..5b232c0413c 100644 --- a/pkg/registry/apis/secret/decrypt/local_client.go +++ b/pkg/registry/apis/secret/decrypt/local_client.go @@ -3,6 +3,8 @@ package decrypt import ( "context" + "github.com/grafana/authlib/types" + "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" ) @@ -19,7 +21,14 @@ func NewLocalDecryptClient(decryptStorage contracts.DecryptStorage) (*LocalDecry }, nil } -func (c *LocalDecryptClient) Decrypt(ctx context.Context, namespace string, names ...string) (map[string]contracts.DecryptResult, error) { +func (c *LocalDecryptClient) Decrypt(ctx context.Context, serviceName, namespace string, names []string) (map[string]contracts.DecryptResult, error) { + ns, err := types.ParseNamespace(namespace) + if err != nil { + return nil, err + } + + ctx = identity.WithServiceIdentityContext(ctx, ns.OrgID, identity.WithServiceIdentityName(serviceName)) + results := make(map[string]contracts.DecryptResult, len(names)) for _, name := range names { diff --git a/pkg/registry/apis/secret/decrypt/service.go b/pkg/registry/apis/secret/decrypt/service.go index 91706a2aae1..c0d014109d9 100644 --- a/pkg/registry/apis/secret/decrypt/service.go +++ b/pkg/registry/apis/secret/decrypt/service.go @@ -43,7 +43,7 @@ func ProvideDecryptService(cfg *setting.Cfg, tracer trace.Tracer, decryptStorage tlsConfig := readTLSFromConfig(cfg) - client, err := NewGRPCDecryptClientWithTLS(tokenExchangeClient, tracer, grpcClientConfig.TokenNamespace, cfg.SecretsManagement.DecryptServerAddress, tlsConfig) + client, err := NewGRPCDecryptClientWithTLS(tokenExchangeClient, tracer, cfg.SecretsManagement.DecryptServerAddress, tlsConfig) if err != nil { return nil, fmt.Errorf("failed to create grpc decrypt client: %w", err) } diff --git a/pkg/registry/apis/secret/decrypt/service_test.go b/pkg/registry/apis/secret/decrypt/service_test.go index 53091f27bc4..d108d0d15ff 100644 --- a/pkg/registry/apis/secret/decrypt/service_test.go +++ b/pkg/registry/apis/secret/decrypt/service_test.go @@ -20,7 +20,6 @@ import ( "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" - "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/grafana/grafana/pkg/services/authn/clients" @@ -49,7 +48,7 @@ func TestDecryptService(t *testing.T) { decryptService, err := ProvideDecryptService(cfg, tracer, mockStorage) require.NoError(t, err) - resp, err := decryptService.Decrypt(ctx, "default", "secure-value-1") + resp, err := decryptService.Decrypt(ctx, "svc-name", "default", []string{"secure-value-1"}) require.NotNil(t, resp) require.NoError(t, err) require.EqualValues(t, decryptedValuesResp, resp) @@ -78,7 +77,7 @@ func TestDecryptService(t *testing.T) { decryptService, err := ProvideDecryptService(cfg, tracer, mockStorage) require.NoError(t, err) - resp, err := decryptService.Decrypt(ctx, "default", "secure-value-1", "secure-value-2") + resp, err := decryptService.Decrypt(ctx, "svc-name", "default", []string{"secure-value-1", "secure-value-2"}) require.NotNil(t, resp) require.NoError(t, err) require.EqualValues(t, decryptedValuesResp, resp) @@ -106,7 +105,7 @@ func TestDecryptService(t *testing.T) { decryptService, err := ProvideDecryptService(cfg, tracer, mockStorage) require.NoError(t, err) - resp, err := decryptService.Decrypt(ctx, "default", "secure-value-1", "secure-value-2") + resp, err := decryptService.Decrypt(ctx, "svc-name", "default", []string{"secure-value-1", "secure-value-2"}) require.NotNil(t, resp) require.NoError(t, err) require.EqualValues(t, decryptedValuesResp, resp) @@ -236,9 +235,8 @@ func TestDecryptService(t *testing.T) { t.Cleanup(func() { require.NoError(t, decryptService.Close()) }) svcIdentity := "provsysoning-test" - authCtx := identity.WithServiceIdentityContext(ctx, 1, identity.WithServiceIdentityName(svcIdentity)) - result, err := decryptService.Decrypt(authCtx, namespace, "secure-value-1") + result, err := decryptService.Decrypt(t.Context(), svcIdentity, namespace, []string{"secure-value-1"}) require.NoError(t, err) require.NotNil(t, result) require.Len(t, result, 1) diff --git a/pkg/registry/apis/secret/service/inline_secure_value.go b/pkg/registry/apis/secret/service/inline_secure_value.go index 062f601ba6e..a02b9c5bf97 100644 --- a/pkg/registry/apis/secret/service/inline_secure_value.go +++ b/pkg/registry/apis/secret/service/inline_secure_value.go @@ -5,9 +5,11 @@ import ( "errors" "fmt" + "github.com/grafana/authlib/authn" authlib "github.com/grafana/authlib/types" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1" @@ -15,6 +17,7 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" + "github.com/grafana/grafana/pkg/util" ) type inlineSecureValueService struct { @@ -35,13 +38,14 @@ func ProvideInlineSecureValueService( } } -func (s *inlineSecureValueService) CanReference(ctx context.Context, owner common.ObjectReference, values common.InlineSecureValues) error { +func (s *inlineSecureValueService) CanReference(ctx context.Context, owner common.ObjectReference, names ...string) error { ctx, span := s.tracer.Start(ctx, "InlineSecureValueService.CanReference", trace.WithAttributes( attribute.String("owner.namespace", owner.Namespace), attribute.String("owner.apiGroup", owner.APIGroup), attribute.String("owner.apiVersion", owner.APIVersion), attribute.String("owner.kind", owner.Kind), attribute.String("owner.name", owner.Name), + attribute.StringSlice("secureValueNames", names), )) defer span.End() @@ -58,31 +62,23 @@ func (s *inlineSecureValueService) CanReference(ctx context.Context, owner commo return fmt.Errorf("owner reference must have a valid API group, API version, kind and name") } - if len(values) == 0 { + if len(names) == 0 { return fmt.Errorf("no inline secure values provided") } - for field, value := range values { - if value.Name == "" { - return fmt.Errorf("field %s has an empty secure value name", field) + for _, name := range names { + if name == "" { + return fmt.Errorf("empty secure value name") } - if !value.Create.IsZero() { - return fmt.Errorf("field %s has 'create' set, which is not allowed", field) - } - - if value.Remove { - return fmt.Errorf("field %s has 'remove' set, which is not allowed", field) - } - - owned, err := s.isSecureValueOwnedByResource(ctx, owner, value.Name) + owned, err := s.isSecureValueOwnedByResource(ctx, owner, name) if err != nil { - return fmt.Errorf("field %s had an error checking secure value ownership: %w", field, err) + return err } if !owned { - if err := s.canIdentityReadSecureValue(ctx, xkube.Namespace(owner.Namespace), value.Name); err != nil { - return fmt.Errorf("field %s: identity cannot read secure value %s: %w", field, value.Name, err) + if err := s.canIdentityReadSecureValue(ctx, xkube.Namespace(owner.Namespace), name); err != nil { + return err } } } @@ -121,7 +117,10 @@ func (s *inlineSecureValueService) isSecureValueOwnedByResource(ctx context.Cont return true, nil // The secure value is owned by the same owner reference, pass! } - return false, fmt.Errorf("secure value %s is not owned by %v but by %v", name, owner, actualOwner) + return false, fmt.Errorf( + "secure value %s is not owned by %s/%s/%s/%s but by %s/%s/%s", + name, owner.APIGroup, owner.APIVersion, owner.Kind, owner.Name, actualOwner.APIVersion, actualOwner.Kind, actualOwner.Name, + ) } // not owned @@ -158,9 +157,103 @@ func (s *inlineSecureValueService) canIdentityReadSecureValue(ctx context.Contex } func (s *inlineSecureValueService) CreateInline(ctx context.Context, owner common.ObjectReference, value common.RawSecureValue) (string, error) { - return "", fmt.Errorf("not implemented yet") + ctx, span := s.tracer.Start(ctx, "InlineSecureValueService.CreateInline", trace.WithAttributes( + attribute.String("owner.namespace", owner.Namespace), + attribute.String("owner.apiGroup", owner.APIGroup), + attribute.String("owner.apiVersion", owner.APIVersion), + attribute.String("owner.kind", owner.Kind), + attribute.String("owner.name", owner.Name), + )) + defer span.End() + + authInfo, ok := authlib.AuthInfoFrom(ctx) + if !ok { + return "", fmt.Errorf("missing auth info in context") + } + + if authInfo.GetIdentityType() != authlib.TypeUser && authInfo.GetIdentityType() != authlib.TypeServiceAccount { + return "", fmt.Errorf("identity type %s not allowed, expected either %s or %s", authInfo.GetIdentityType(), authlib.TypeUser, authlib.TypeServiceAccount) + } + + serviceIdentityList, ok := authInfo.GetExtra()[authn.ServiceIdentityKey] + if !ok || len(serviceIdentityList) != 1 { + return "", fmt.Errorf("expected exactly one service identity, found %d", len(serviceIdentityList)) + } + serviceIdentity := serviceIdentityList[0] + + if owner.Namespace == "" || !authlib.NamespaceMatches(authInfo.GetNamespace(), owner.Namespace) { + return "", fmt.Errorf("owner namespace %s does not match auth info namespace %s", owner.Namespace, authInfo.GetNamespace()) + } + + if owner.APIGroup == "" || owner.APIVersion == "" || owner.Kind == "" || owner.Name == "" { + return "", fmt.Errorf("owner reference must have a valid API group, API version, kind and name") + } + + if value.IsZero() { + return "", fmt.Errorf("trying to create an inline secure value with empty value") + } + + // TODO(2025-07-31): when we migrate to using the common type, we don't need this conversion. + secret := secretv1beta1.ExposedSecureValue(value) + + spec := &secretv1beta1.SecureValue{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sv-" + util.GenerateShortUID(), + Namespace: owner.Namespace, + OwnerReferences: []metav1.OwnerReference{owner.ToOwnerReference()}, + }, + Spec: secretv1beta1.SecureValueSpec{ + Description: fmt.Sprintf("Inline secure value for %s/%s in %s/%s", owner.Kind, owner.Name, owner.APIGroup, owner.APIVersion), + Value: &secret, + Decrypters: []string{ + serviceIdentity, + }, + }, + } + + createdSv, err := s.secureValueService.Create(ctx, spec, authInfo.GetUID()) + if err != nil { + return "", fmt.Errorf("error creating secure value %s for owner %v: %w", spec.Name, owner, err) + } + + return createdSv.GetName(), nil } func (s *inlineSecureValueService) DeleteWhenOwnedByResource(ctx context.Context, owner common.ObjectReference, name string) error { - return fmt.Errorf("not implemented yet") + ctx, span := s.tracer.Start(ctx, "InlineSecureValueService.DeleteWhenOwnedByResource", trace.WithAttributes( + attribute.String("owner.namespace", owner.Namespace), + attribute.String("owner.apiGroup", owner.APIGroup), + attribute.String("owner.apiVersion", owner.APIVersion), + attribute.String("owner.kind", owner.Kind), + attribute.String("owner.name", owner.Name), + attribute.String("secureValue.name", name), + )) + defer span.End() + + authInfo, ok := authlib.AuthInfoFrom(ctx) + if !ok { + return fmt.Errorf("missing auth info in context") + } + + if owner.Namespace == "" || !authlib.NamespaceMatches(authInfo.GetNamespace(), owner.Namespace) { + return fmt.Errorf("owner namespace %s does not match auth info namespace %s", owner.Namespace, authInfo.GetNamespace()) + } + + if owner.APIGroup == "" || owner.APIVersion == "" || owner.Kind == "" || owner.Name == "" { + return fmt.Errorf("owner reference must have a valid API group, API version, kind and name") + } + + owned, err := s.isSecureValueOwnedByResource(ctx, owner, name) + if err != nil { + return fmt.Errorf("error checking if secure value %s is owned by %v: %w", name, owner, err) + } + + if owned { + if _, err := s.secureValueService.Delete(ctx, xkube.Namespace(owner.Namespace), name); err != nil { + return fmt.Errorf("error deleting secure value %s for owner %v: %w", name, owner, err) + } + } + + // if it is not owned, this is a no-op + return nil } diff --git a/pkg/registry/apis/secret/service/inline_secure_value_test.go b/pkg/registry/apis/secret/service/inline_secure_value_test.go index a358ff13cbd..e20885b0cc3 100644 --- a/pkg/registry/apis/secret/service/inline_secure_value_test.go +++ b/pkg/registry/apis/secret/service/inline_secure_value_test.go @@ -5,8 +5,10 @@ import ( common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/grafana/grafana/pkg/registry/apis/secret/service" "github.com/grafana/grafana/pkg/registry/apis/secret/testutils" + "github.com/grafana/grafana/pkg/registry/apis/secret/xkube" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace/noop" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -48,18 +50,13 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) { require.NoError(t, err) require.NotNil(t, createdSv2) - values := common.InlineSecureValues{ - "fieldA": {Name: sv1}, - "fieldB": {Name: sv2}, - } - ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{ "securevalues:read": {"securevalues:uid:" + sv2}, }) svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, tu.AccessClient) - err = svc.CanReference(ctx, owner, values) + err = svc.CanReference(ctx, owner, sv1, sv2) require.NoError(t, err) }) @@ -67,7 +64,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) { t.Parallel() svc := service.ProvideInlineSecureValueService(tracer, nil, nil) - err := svc.CanReference(t.Context(), common.ObjectReference{}, common.InlineSecureValues{}) + err := svc.CanReference(t.Context(), common.ObjectReference{}) require.Error(t, err) }) @@ -79,7 +76,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) { reqNs := "org-2345" ctx := testutils.CreateUserAuthContext(t.Context(), reqNs, map[string][]string{}) - err := svc.CanReference(ctx, owner, common.InlineSecureValues{}) + err := svc.CanReference(ctx, owner) require.Error(t, err) }) @@ -90,7 +87,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) { ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{}) - err := svc.CanReference(ctx, common.ObjectReference{}, common.InlineSecureValues{}) + err := svc.CanReference(ctx, common.ObjectReference{}) require.Error(t, err) }) @@ -105,23 +102,23 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) { ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{}) - err := svc.CanReference(ctx, owner, common.InlineSecureValues{}) + err := svc.CanReference(ctx, owner) require.Error(t, err) owner.APIGroup = "prometheus.datasource.grafana.app" - require.Error(t, svc.CanReference(ctx, owner, common.InlineSecureValues{})) + require.Error(t, svc.CanReference(ctx, owner)) owner.APIGroup = "" owner.APIVersion = "v1alpha1" - require.Error(t, svc.CanReference(ctx, owner, common.InlineSecureValues{})) + require.Error(t, svc.CanReference(ctx, owner)) owner.APIVersion = "" owner.Kind = "DataSourceConfig" - require.Error(t, svc.CanReference(ctx, owner, common.InlineSecureValues{})) + require.Error(t, svc.CanReference(ctx, owner)) owner.Kind = "" owner.Name = "test-datasource" - require.Error(t, svc.CanReference(ctx, owner, common.InlineSecureValues{})) + require.Error(t, svc.CanReference(ctx, owner)) owner.Name = "" }) @@ -132,58 +129,7 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) { ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{}) - err := svc.CanReference(ctx, owner, common.InlineSecureValues{}) - require.Error(t, err) - }) - - t.Run("when one of the secure values does not have a `name`, it returns an error", func(t *testing.T) { - t.Parallel() - - svc := service.ProvideInlineSecureValueService(tracer, nil, nil) - - values := common.InlineSecureValues{ - "fieldA": {Name: ""}, - } - - ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{}) - - err := svc.CanReference(ctx, owner, values) - require.Error(t, err) - }) - - t.Run("when one of the secure values has `create` field set, it returns an error", func(t *testing.T) { - t.Parallel() - - svc := service.ProvideInlineSecureValueService(tracer, nil, nil) - - values := common.InlineSecureValues{ - "fieldA": { - Name: "test-sv", - Create: common.NewSecretValue("test"), - }, - } - - ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{}) - - err := svc.CanReference(ctx, owner, values) - require.Error(t, err) - }) - - t.Run("when one of the secure values has `remove` field set, it returns an error", func(t *testing.T) { - t.Parallel() - - svc := service.ProvideInlineSecureValueService(tracer, nil, nil) - - values := common.InlineSecureValues{ - "fieldA": { - Name: "test-sv", - Remove: true, - }, - } - - ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{}) - - err := svc.CanReference(ctx, owner, values) + err := svc.CanReference(ctx, owner) require.Error(t, err) }) @@ -193,13 +139,9 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) { tu := testutils.Setup(t) svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil) - values := common.InlineSecureValues{ - "fieldA": {Name: "non-existent-sv"}, - } - ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{}) - err := svc.CanReference(ctx, owner, values) + err := svc.CanReference(ctx, owner, "non-existent-sv") require.Error(t, err) }) @@ -225,15 +167,11 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) { require.NoError(t, err) require.NotNil(t, createdSv1) - values := common.InlineSecureValues{ - "fieldA": {Name: sv1}, - } - ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{}) svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil) - err = svc.CanReference(ctx, owner, values) + err = svc.CanReference(ctx, owner, sv1) require.Error(t, err) }) @@ -249,15 +187,11 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) { }) require.NoError(t, err) - values := common.InlineSecureValues{ - "fieldA": {Name: sv1}, - } - ctx := identity.WithServiceIdentityContext(t.Context(), 1234) svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil) - err = svc.CanReference(ctx, owner, values) + err = svc.CanReference(ctx, owner, sv1) require.Error(t, err) }) @@ -273,22 +207,304 @@ func TestIntegration_InlineSecureValue_CanReference(t *testing.T) { }) require.NoError(t, err) - values := common.InlineSecureValues{ - "fieldA": {Name: sv1}, - } - svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, tu.AccessClient) ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{ "securevalues:read": {"securevalues:uid:another-sv"}, // can read, but another resource! }) - err = svc.CanReference(ctx, owner, values) + err = svc.CanReference(ctx, owner, sv1) require.Error(t, err) ctx = testutils.CreateUserAuthContext(t.Context(), defaultNs, nil) - err = svc.CanReference(ctx, owner, values) + err = svc.CanReference(ctx, owner, sv1) require.Error(t, err) }) } + +func TestIntegration_InlineSecureValue_CreateInline(t *testing.T) { + t.Parallel() + + tracer := noop.NewTracerProvider().Tracer("test") + + defaultNs := "org-1234" + owner := common.ObjectReference{ + APIGroup: "prometheus.datasource.grafana.app", + APIVersion: "v1alpha1", + Kind: "DataSourceConfig", + Name: "test-datasource", + Namespace: defaultNs, + } + + t.Run("happy path creates an inline secure value", func(t *testing.T) { + t.Parallel() + + tu := testutils.Setup(t) + + secret := common.NewSecretValue("test-value") + + serviceIdentity := "service-identity" + + createAuthCtx := testutils.CreateOBOAuthContext(t.Context(), serviceIdentity, owner.Namespace, nil, nil) + + svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil) + + createdName, err := svc.CreateInline(createAuthCtx, owner, secret) + require.NoError(t, err) + require.NotEmpty(t, createdName) + + decryptedValues, err := tu.DecryptService.Decrypt(t.Context(), serviceIdentity, owner.Namespace, []string{createdName}) + require.NoError(t, err) + + decryptedResult, ok := decryptedValues[createdName] + require.True(t, ok) + require.Equal(t, decryptedResult.Value().DangerouslyExposeAndConsumeValue(), secret.DangerouslyExposeAndConsumeValue()) + }) + + t.Run("when the auth info is missing it returns an error", func(t *testing.T) { + t.Parallel() + + svc := service.ProvideInlineSecureValueService(tracer, nil, nil) + _, err := svc.CreateInline(t.Context(), common.ObjectReference{}, "") + require.Error(t, err) + }) + + t.Run("when the request identity is not a user nor a service account, it returns an error", func(t *testing.T) { + t.Parallel() + + svc := service.ProvideInlineSecureValueService(tracer, nil, nil) + + createAuthCtx := testutils.CreateServiceAuthContext(t.Context(), "service-identity", defaultNs, nil) + + _, err := svc.CreateInline(createAuthCtx, common.ObjectReference{}, "") + require.Error(t, err) + }) + + t.Run("when the owner namespace does not match auth info namespace it returns an error", func(t *testing.T) { + t.Parallel() + + svc := service.ProvideInlineSecureValueService(tracer, nil, nil) + + reqNs := "org-2345" + createAuthCtx := testutils.CreateOBOAuthContext(t.Context(), "service-identity", reqNs, nil, nil) + + _, err := svc.CreateInline(createAuthCtx, owner, "") + require.Error(t, err) + }) + + t.Run("when the owner namespace is empty it returns an error", func(t *testing.T) { + t.Parallel() + + svc := service.ProvideInlineSecureValueService(tracer, nil, nil) + + createAuthCtx := testutils.CreateOBOAuthContext(t.Context(), "service-identity", defaultNs, nil, nil) + + _, err := svc.CreateInline(createAuthCtx, common.ObjectReference{}, "") + require.Error(t, err) + }) + + t.Run("when the owner reference has empty fields it returns an error", func(t *testing.T) { + t.Parallel() + + svc := service.ProvideInlineSecureValueService(tracer, nil, nil) + + owner := common.ObjectReference{ + Namespace: defaultNs, + } + + createAuthCtx := testutils.CreateOBOAuthContext(t.Context(), "service-identity", defaultNs, nil, nil) + + _, err := svc.CreateInline(createAuthCtx, owner, "") + require.Error(t, err) + + owner.APIGroup = "prometheus.datasource.grafana.app" + _, err = svc.CreateInline(createAuthCtx, owner, "") + require.Error(t, err) + + owner.APIVersion = "v1alpha1" + _, err = svc.CreateInline(createAuthCtx, owner, "") + require.Error(t, err) + + owner.Kind = "DataSourceConfig" + _, err = svc.CreateInline(createAuthCtx, owner, "") + require.Error(t, err) + owner.Kind = "" + + owner.Name = "test-datasource" + _, err = svc.CreateInline(createAuthCtx, owner, "") + require.Error(t, err) + }) + + t.Run("when an empty secret is provided it returns an error", func(t *testing.T) { + t.Parallel() + + svc := service.ProvideInlineSecureValueService(tracer, nil, nil) + + createAuthCtx := testutils.CreateOBOAuthContext(t.Context(), "service-identity", defaultNs, nil, nil) + + _, err := svc.CreateInline(createAuthCtx, owner, "") + require.Error(t, err) + }) +} + +func TestIntegration_InlineSecureValue_DeleteWhenOwnedByResource(t *testing.T) { + t.Parallel() + + tracer := noop.NewTracerProvider().Tracer("test") + + defaultNs := "org-1234" + owner := common.ObjectReference{ + APIGroup: "prometheus.datasource.grafana.app", + APIVersion: "v1alpha1", + Kind: "DataSourceConfig", + Name: "test-datasource", + Namespace: defaultNs, + } + + t.Run("happy path deletes an owned secure value", func(t *testing.T) { + t.Parallel() + + tu := testutils.Setup(t) + + sv1 := "test-secure-value-1" + createdSv1, err := tu.CreateSv(t.Context(), func(cfg *testutils.CreateSvConfig) { + cfg.Sv.Name = sv1 + cfg.Sv.Namespace = defaultNs + cfg.Sv.OwnerReferences = []metav1.OwnerReference{owner.ToOwnerReference()} + }) + require.NoError(t, err) + require.NotNil(t, createdSv1) + + svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil) + + ctx := testutils.CreateServiceAuthContext(t.Context(), "", defaultNs, nil) + + err = svc.DeleteWhenOwnedByResource(ctx, owner, sv1) + require.NoError(t, err) + + // make sure it got deleted + sv, err := tu.SecureValueService.Read(ctx, xkube.Namespace(owner.Namespace), sv1) + require.ErrorIs(t, err, contracts.ErrSecureValueNotFound) + require.Nil(t, sv) + }) + + t.Run("when the auth info is missing it returns an error", func(t *testing.T) { + t.Parallel() + + svc := service.ProvideInlineSecureValueService(tracer, nil, nil) + err := svc.DeleteWhenOwnedByResource(t.Context(), common.ObjectReference{}, "") + require.Error(t, err) + }) + + t.Run("when the owner namespace does not match auth info namespace it returns an error", func(t *testing.T) { + t.Parallel() + + svc := service.ProvideInlineSecureValueService(tracer, nil, nil) + + reqNs := "org-2345" + ctx := testutils.CreateUserAuthContext(t.Context(), reqNs, map[string][]string{}) + + err := svc.DeleteWhenOwnedByResource(ctx, owner, "") + require.Error(t, err) + }) + + t.Run("when the owner namespace is empty it returns an error", func(t *testing.T) { + t.Parallel() + + svc := service.ProvideInlineSecureValueService(tracer, nil, nil) + + ctx := testutils.CreateUserAuthContext(t.Context(), defaultNs, map[string][]string{}) + + err := svc.DeleteWhenOwnedByResource(ctx, common.ObjectReference{}, "") + require.Error(t, err) + }) + + t.Run("when the owner reference has empty fields it returns an error", func(t *testing.T) { + t.Parallel() + + svc := service.ProvideInlineSecureValueService(tracer, nil, nil) + + owner := common.ObjectReference{ + Namespace: defaultNs, + } + + createAuthCtx := testutils.CreateOBOAuthContext(t.Context(), "service-identity", defaultNs, nil, nil) + + require.Error(t, svc.DeleteWhenOwnedByResource(createAuthCtx, owner, "")) + + owner.APIGroup = "prometheus.datasource.grafana.app" + require.Error(t, svc.DeleteWhenOwnedByResource(createAuthCtx, owner, "")) + + owner.APIVersion = "v1alpha1" + require.Error(t, svc.DeleteWhenOwnedByResource(createAuthCtx, owner, "")) + + owner.Kind = "DataSourceConfig" + require.Error(t, svc.DeleteWhenOwnedByResource(createAuthCtx, owner, "")) + owner.Kind = "" + + owner.Name = "test-datasource" + require.Error(t, svc.DeleteWhenOwnedByResource(createAuthCtx, owner, "")) + }) + + t.Run("when the secure value exists but the owner does not match, it returns an error", func(t *testing.T) { + t.Parallel() + + tu := testutils.Setup(t) + + sv1 := "test-secure-value-1" + createdSv1, err := tu.CreateSv(t.Context(), func(cfg *testutils.CreateSvConfig) { + cfg.Sv.Name = sv1 + cfg.Sv.Namespace = defaultNs + cfg.Sv.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: "another.example.com/v0alpha1", + Kind: "another-kind", + Name: "another-name", + }, + } + }) + require.NoError(t, err) + require.NotNil(t, createdSv1) + + svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil) + + ctx := testutils.CreateServiceAuthContext(t.Context(), "", defaultNs, nil) + + err = svc.DeleteWhenOwnedByResource(ctx, owner, sv1) + require.Error(t, err) + + // make sure it still exists + sv, err := tu.SecureValueService.Read(ctx, xkube.Namespace(owner.Namespace), sv1) + require.NoError(t, err) + require.NotNil(t, sv) + require.Equal(t, sv1, sv.GetName()) + }) + + t.Run("when the secure value exists but it is shared (no owner), it does not return an error (noop)", func(t *testing.T) { + t.Parallel() + + tu := testutils.Setup(t) + + sv1 := "test-secure-value-1" + createdSv1, err := tu.CreateSv(t.Context(), func(cfg *testutils.CreateSvConfig) { + cfg.Sv.Name = sv1 + cfg.Sv.Namespace = defaultNs + }) + require.NoError(t, err) + require.NotNil(t, createdSv1) + + svc := service.ProvideInlineSecureValueService(tracer, tu.SecureValueService, nil) + + ctx := testutils.CreateServiceAuthContext(t.Context(), "", defaultNs, nil) + + err = svc.DeleteWhenOwnedByResource(ctx, owner, sv1) + require.NoError(t, err) + + // make sure it still exists + sv, err := tu.SecureValueService.Read(ctx, xkube.Namespace(owner.Namespace), sv1) + require.NoError(t, err) + require.NotNil(t, sv) + require.Equal(t, sv1, sv.GetName()) + }) +} diff --git a/pkg/registry/apis/secret/testutils/testutils.go b/pkg/registry/apis/secret/testutils/testutils.go index ebe8b109b81..4c000e76908 100644 --- a/pkg/registry/apis/secret/testutils/testutils.go +++ b/pkg/registry/apis/secret/testutils/testutils.go @@ -244,8 +244,9 @@ func CreateUserAuthContext(ctx context.Context, namespace string, permissions ma return types.WithAuthInfo(ctx, requester) } -func CreateServiceAuthContext(ctx context.Context, serviceIdentity string, permissions []string) context.Context { +func CreateServiceAuthContext(ctx context.Context, serviceIdentity string, namespace string, permissions []string) context.Context { requester := &identity.StaticRequester{ + Namespace: namespace, AccessTokenClaims: &authn.Claims[authn.AccessTokenClaims]{ Rest: authn.AccessTokenClaims{ Permissions: permissions, @@ -256,3 +257,33 @@ func CreateServiceAuthContext(ctx context.Context, serviceIdentity string, permi return types.WithAuthInfo(ctx, requester) } + +// CreateOBOAuthContext emulates a context where the request is made on-behalf-of (OBO) a user, with an access token. +func CreateOBOAuthContext( + ctx context.Context, + serviceIdentity string, + namespace string, + userPermissions map[string][]string, + delegatedPermissions []string, +) context.Context { + requester := &identity.StaticRequester{ + Namespace: namespace, + Type: types.TypeUser, + OrgID: 1, + UserID: 1, + Permissions: map[int64]map[string][]string{ + 1: userPermissions, + }, + AccessTokenClaims: &authn.Claims[authn.AccessTokenClaims]{ + Rest: authn.AccessTokenClaims{ + ServiceIdentity: serviceIdentity, + DelegatedPermissions: delegatedPermissions, + Actor: &authn.ActorClaims{ + Subject: "user:1", + }, + }, + }, + } + + return types.WithAuthInfo(ctx, requester) +} diff --git a/pkg/registry/apps/apps.go b/pkg/registry/apps/apps.go index fa7607102f8..62aec11ad97 100644 --- a/pkg/registry/apps/apps.go +++ b/pkg/registry/apps/apps.go @@ -14,6 +14,7 @@ import ( "github.com/grafana/grafana/pkg/registry/apps/alerting/notifications" "github.com/grafana/grafana/pkg/registry/apps/investigations" "github.com/grafana/grafana/pkg/registry/apps/playlist" + "github.com/grafana/grafana/pkg/registry/apps/shorturl" "github.com/grafana/grafana/pkg/services/apiserver" "github.com/grafana/grafana/pkg/services/apiserver/builder" "github.com/grafana/grafana/pkg/services/apiserver/builder/runner" @@ -24,9 +25,15 @@ import ( // ProvideAppInstallers returns a list of app installers that can be used to install apps. // This is the pattern that should be used to provide app installers in the app registry. func ProvideAppInstallers( + features featuremgmt.FeatureToggles, playlistAppInstaller *playlist.PlaylistAppInstaller, + shorturlAppInstaller *shorturl.ShortURLAppInstaller, ) []appsdkapiserver.AppInstaller { - return []appsdkapiserver.AppInstaller{playlistAppInstaller} + installers := []appsdkapiserver.AppInstaller{playlistAppInstaller} + if features.IsEnabledGlobally(featuremgmt.FlagKubernetesShortURLs) { + installers = append(installers, shorturlAppInstaller) + } + return installers } var ( diff --git a/pkg/registry/apps/playlist/register.go b/pkg/registry/apps/playlist/register.go index 4374c0265dd..123974c2dcc 100644 --- a/pkg/registry/apps/playlist/register.go +++ b/pkg/registry/apps/playlist/register.go @@ -26,7 +26,6 @@ import ( var ( _ appsdkapiserver.AppInstaller = (*PlaylistAppInstaller)(nil) _ appinstaller.LegacyStorageProvider = (*PlaylistAppInstaller)(nil) - _ appinstaller.APIEnablementProvider = (*PlaylistAppInstaller)(nil) ) type PlaylistAppInstaller struct { @@ -102,10 +101,3 @@ func (p *PlaylistAppInstaller) GetLegacyStorage(requested schema.GroupVersionRes ) return legacyStore } - -// GetAllowedV0Alpha1Resources returns the list of resources that are allowed to be accessed in v0alpha1. -func (p *PlaylistAppInstaller) GetAllowedV0Alpha1Resources() []string { - return []string{ - playlistv0alpha1.PlaylistKind().Plural(), - } -} diff --git a/pkg/registry/apps/shorturl/conversions.go b/pkg/registry/apps/shorturl/conversions.go new file mode 100644 index 00000000000..621f3618000 --- /dev/null +++ b/pkg/registry/apps/shorturl/conversions.go @@ -0,0 +1,34 @@ +package shorturl + +import ( + "fmt" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + + shorturl "github.com/grafana/grafana/apps/shorturl/pkg/apis/shorturl/v1alpha1" + "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" + "github.com/grafana/grafana/pkg/services/shorturls" +) + +func convertToK8sResource(v *shorturls.ShortUrl, namespacer request.NamespaceMapper) *shorturl.ShortURL { + spec := shorturl.ShortURLSpec{ + Path: v.Path, + } + status := shorturl.ShortURLStatus{ + LastSeenAt: v.LastSeenAt, + } + p := &shorturl.ShortURL{ + ObjectMeta: metav1.ObjectMeta{ + Name: v.Uid, + UID: types.UID(v.Uid), + ResourceVersion: fmt.Sprintf("%d", v.LastSeenAt), + CreationTimestamp: metav1.NewTime(time.UnixMilli(v.CreatedAt)), + Namespace: namespacer(v.OrgId), + }, + Spec: spec, + Status: status, + } + return p +} diff --git a/pkg/registry/apps/shorturl/legacy_storage.go b/pkg/registry/apps/shorturl/legacy_storage.go new file mode 100644 index 00000000000..0b7948d62a5 --- /dev/null +++ b/pkg/registry/apps/shorturl/legacy_storage.go @@ -0,0 +1,145 @@ +package shorturl + +import ( + "context" + "errors" + "fmt" + "strings" + + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apiserver/pkg/registry/rest" + + shorturl "github.com/grafana/grafana/apps/shorturl/pkg/apis/shorturl/v1alpha1" + "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" + "github.com/grafana/grafana/pkg/services/authn" + "github.com/grafana/grafana/pkg/services/shorturls" + "github.com/grafana/grafana/pkg/services/user" +) + +var ( + _ rest.Scoper = (*legacyStorage)(nil) + _ rest.SingularNameProvider = (*legacyStorage)(nil) + _ rest.Getter = (*legacyStorage)(nil) + _ rest.Storage = (*legacyStorage)(nil) + _ rest.Creater = (*legacyStorage)(nil) + _ rest.Updater = (*legacyStorage)(nil) + _ rest.GracefulDeleter = (*legacyStorage)(nil) +) + +type legacyStorage struct { + service shorturls.Service + namespacer request.NamespaceMapper + tableConverter rest.TableConvertor +} + +func (s *legacyStorage) New() runtime.Object { + return shorturl.ShortURLKind().ZeroValue() +} + +func (s *legacyStorage) Destroy() {} + +func (s *legacyStorage) NamespaceScoped() bool { + return true // namespace == org +} + +func (s *legacyStorage) GetSingularName() string { + return strings.ToLower(shorturl.ShortURLKind().Kind()) +} + +func (s *legacyStorage) NewList() runtime.Object { + return shorturl.ShortURLKind().ZeroListValue() +} + +func (s *legacyStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { + return s.tableConverter.ConvertToTable(ctx, object, tableOptions) +} + +func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) { + return nil, fmt.Errorf("List for shorturl not implemented") +} + +func (s *legacyStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { + requester, err := identity.GetRequester(ctx) + if err != nil { + return nil, err + } + + // Convert identity.Requester to *user.SignedInUser + var signedInUser *user.SignedInUser + if authnIdentity, ok := requester.(*authn.Identity); ok { + signedInUser = authnIdentity.SignedInUser() + } else if userIdentity, ok := requester.(*user.SignedInUser); ok { + signedInUser = userIdentity + } else { + return nil, fmt.Errorf("unsupported identity type") + } + + dto, err := s.service.GetShortURLByUID(ctx, signedInUser, name) + if err != nil || dto == nil { + if errors.Is(err, shorturls.ErrShortURLNotFound) || err == nil { + err = k8serrors.NewNotFound(schema.GroupResource{ + Group: shorturl.ShortURLKind().Group(), + Resource: shorturl.ShortURLKind().Plural(), + }, name) + } + return nil, err + } + + return convertToK8sResource(dto, s.namespacer), nil +} + +func (s *legacyStorage) Create(ctx context.Context, + obj runtime.Object, + createValidation rest.ValidateObjectFunc, + options *metav1.CreateOptions, +) (runtime.Object, error) { + requester, err := identity.GetRequester(ctx) + if err != nil { + return nil, err + } + // Convert identity.Requester to *user.SignedInUser + var signedInUser *user.SignedInUser + if authnIdentity, ok := requester.(*authn.Identity); ok { + signedInUser = authnIdentity.SignedInUser() + } else if userIdentity, ok := requester.(*user.SignedInUser); ok { + signedInUser = userIdentity + } else { + return nil, fmt.Errorf("unsupported identity type") + } + + p, ok := obj.(*shorturl.ShortURL) + if !ok { + return nil, fmt.Errorf("expected shorturl?") + } + out, err := s.service.CreateShortURL(ctx, signedInUser, p.Spec.Path) + if err != nil { + return nil, err + } + return s.Get(ctx, out.Uid, nil) +} + +func (s *legacyStorage) Update(ctx context.Context, + name string, + objInfo rest.UpdatedObjectInfo, + createValidation rest.ValidateObjectFunc, + updateValidation rest.ValidateObjectUpdateFunc, + forceAllowCreate bool, + options *metav1.UpdateOptions, +) (runtime.Object, bool, error) { + return nil, false, fmt.Errorf("Update for shorturl not implemented") +} + +// GracefulDeleter +func (s *legacyStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) { + return nil, false, fmt.Errorf("Delete for shorturl not implemented") +} + +// CollectionDeleter +func (s *legacyStorage) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *internalversion.ListOptions) (runtime.Object, error) { + return nil, fmt.Errorf("DeleteCollection for shorturl not implemented") +} diff --git a/pkg/registry/apps/shorturl/register.go b/pkg/registry/apps/shorturl/register.go new file mode 100644 index 00000000000..e280cd86a9e --- /dev/null +++ b/pkg/registry/apps/shorturl/register.go @@ -0,0 +1,97 @@ +package shorturl + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + restclient "k8s.io/client-go/rest" + + "github.com/grafana/grafana-app-sdk/app" + appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver" + "github.com/grafana/grafana-app-sdk/simple" + + "github.com/grafana/grafana/apps/shorturl/pkg/apis" + shorturl "github.com/grafana/grafana/apps/shorturl/pkg/apis/shorturl/v1alpha1" + shorturlapp "github.com/grafana/grafana/apps/shorturl/pkg/app" + "github.com/grafana/grafana/pkg/apimachinery/utils" + grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" + "github.com/grafana/grafana/pkg/services/apiserver/appinstaller" + "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" + "github.com/grafana/grafana/pkg/services/shorturls" + "github.com/grafana/grafana/pkg/setting" +) + +var ( + _ appsdkapiserver.AppInstaller = (*ShortURLAppInstaller)(nil) + _ appinstaller.LegacyStorageProvider = (*ShortURLAppInstaller)(nil) +) + +type ShortURLAppInstaller struct { + appsdkapiserver.AppInstaller + cfg *setting.Cfg + service shorturls.Service +} + +func RegisterAppInstaller( + cfg *setting.Cfg, + service shorturls.Service, +) (*ShortURLAppInstaller, error) { + installer := &ShortURLAppInstaller{ + cfg: cfg, + service: service, + } + specificConfig := any(&shorturlapp.ShortURLConfig{ + AppURL: cfg.AppURL, + }) + provider := simple.NewAppProvider(apis.LocalManifest(), specificConfig, shorturlapp.New) + + appCfg := app.Config{ + KubeConfig: restclient.Config{}, // this will be overridden by the installer's InitializeApp method + ManifestData: *apis.LocalManifest().ManifestData, + SpecificConfig: specificConfig, + } + i, err := appsdkapiserver.NewDefaultAppInstaller(provider, appCfg, apis.ManifestGoTypeAssociator, apis.ManifestCustomRouteResponsesAssociator) + if err != nil { + return nil, err + } + installer.AppInstaller = i + return installer, nil +} + +func (s *ShortURLAppInstaller) GetLegacyStorage(requested schema.GroupVersionResource) grafanarest.Storage { + gvr := schema.GroupVersionResource{ + Group: shorturl.ShortURLKind().Group(), + Version: shorturl.ShortURLKind().Version(), + Resource: shorturl.ShortURLKind().Plural(), + } + if requested.String() != gvr.String() { + return nil + } + legacyStore := &legacyStorage{ + service: s.service, + namespacer: request.GetNamespaceMapper(s.cfg), + } + legacyStore.tableConverter = utils.NewTableConverter( + gvr.GroupResource(), + utils.TableColumns{ + Definition: []metav1.TableColumnDefinition{ + {Name: "Name", Type: "string", Format: "name"}, + {Name: "Path", Type: "string", Format: "string", Description: "The url path"}, + {Name: "Last Seen At", Type: "number"}, + }, + Reader: func(obj any) ([]interface{}, error) { + m, ok := obj.(*shorturl.ShortURL) + if !ok { + return nil, fmt.Errorf("expected shorturl") + } + return []interface{}{ + m.Name, + m.Spec.Path, + m.Status.LastSeenAt, + }, nil + }, + }, + ) + return legacyStore +} diff --git a/pkg/registry/apps/wireset.go b/pkg/registry/apps/wireset.go index 48212655418..b7ff8837665 100644 --- a/pkg/registry/apps/wireset.go +++ b/pkg/registry/apps/wireset.go @@ -7,6 +7,7 @@ import ( "github.com/grafana/grafana/pkg/registry/apps/alerting/notifications" "github.com/grafana/grafana/pkg/registry/apps/investigations" "github.com/grafana/grafana/pkg/registry/apps/playlist" + "github.com/grafana/grafana/pkg/registry/apps/shorturl" ) var WireSet = wire.NewSet( @@ -16,4 +17,5 @@ var WireSet = wire.NewSet( investigations.RegisterApp, advisor.RegisterApp, notifications.RegisterApp, + shorturl.RegisterAppInstaller, ) diff --git a/pkg/server/wire_gen.go b/pkg/server/wire_gen.go index cf76c869512..5448bd29d20 100644 --- a/pkg/server/wire_gen.go +++ b/pkg/server/wire_gen.go @@ -76,6 +76,7 @@ import ( notifications2 "github.com/grafana/grafana/pkg/registry/apps/alerting/notifications" "github.com/grafana/grafana/pkg/registry/apps/investigations" "github.com/grafana/grafana/pkg/registry/apps/playlist" + "github.com/grafana/grafana/pkg/registry/apps/shorturl" "github.com/grafana/grafana/pkg/registry/backgroundsvcs" "github.com/grafana/grafana/pkg/registry/usagestatssvcs" "github.com/grafana/grafana/pkg/services/accesscontrol" @@ -688,7 +689,11 @@ func Initialize(cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*Ser if err != nil { return nil, err } - v2 := appregistry.ProvideAppInstallers(playlistAppInstaller) + shortURLAppInstaller, err := shorturl.RegisterAppInstaller(cfg, shortURLService) + if err != nil { + return nil, err + } + v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, shortURLAppInstaller) builderMetrics := builder.ProvideBuilderMetrics(registerer) apiserverService, err := apiserver.ProvideService(cfg, featureToggles, routeRegisterImpl, tracingService, serverLockService, sqlStore, kvStore, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, pluginstoreService, dualwriteService, resourceClient, eventualRestConfigProvider, v, eventualRestConfigProvider, registerer, aggregatorRunner, v2, builderMetrics) if err != nil { @@ -1249,7 +1254,11 @@ func InitializeForTest(t sqlutil.ITestDB, testingT interface { if err != nil { return nil, err } - v2 := appregistry.ProvideAppInstallers(playlistAppInstaller) + shortURLAppInstaller, err := shorturl.RegisterAppInstaller(cfg, shortURLService) + if err != nil { + return nil, err + } + v2 := appregistry.ProvideAppInstallers(featureToggles, playlistAppInstaller, shortURLAppInstaller) builderMetrics := builder.ProvideBuilderMetrics(registerer) apiserverService, err := apiserver.ProvideService(cfg, featureToggles, routeRegisterImpl, tracingService, serverLockService, sqlStore, kvStore, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, pluginstoreService, dualwriteService, resourceClient, eventualRestConfigProvider, v, eventualRestConfigProvider, registerer, aggregatorRunner, v2, builderMetrics) if err != nil { diff --git a/pkg/services/apiserver/appinstaller/installer.go b/pkg/services/apiserver/appinstaller/installer.go index fb5427b761f..9817f7513bc 100644 --- a/pkg/services/apiserver/appinstaller/installer.go +++ b/pkg/services/apiserver/appinstaller/installer.go @@ -8,17 +8,19 @@ import ( appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver" "github.com/grafana/grafana-app-sdk/logging" - grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" - "github.com/grafana/grafana/pkg/services/apiserver/builder" - "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" - grafanaapiserveroptions "github.com/grafana/grafana/pkg/services/apiserver/options" "github.com/grafana/grafana/pkg/storage/legacysql/dualwrite" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/registry/generic" genericapiserver "k8s.io/apiserver/pkg/server" + serverstore "k8s.io/apiserver/pkg/server/storage" "k8s.io/kube-openapi/pkg/common" + + grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" + "github.com/grafana/grafana/pkg/services/apiserver/builder" + "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" + grafanaapiserveroptions "github.com/grafana/grafana/pkg/services/apiserver/options" ) type LegacyStorageGetterFunc func(schema.GroupVersionResource) grafanarest.Storage @@ -31,13 +33,6 @@ type AuthorizerProvider interface { GetAuthorizer() authorizer.Authorizer } -type APIEnablementProvider interface { - // Do not implement this unless you have special circumstances! This is a list of resources that are allowed to be accessed in v0alpha1, - // to prevent accidental exposure of experimental APIs. While developing, use the feature flag `grafanaAPIServerWithExperimentalAPIs`. - // And then, when you're ready to expose this to the end user, go to v1beta1 instead. - GetAllowedV0Alpha1Resources() []string -} - type AppInstallerConfig struct { CustomConfig any AllowedV0Alpha1Resources []string @@ -132,6 +127,7 @@ func InstallAPIs( dualWriteService dualwrite.Service, dualWriterMetrics *grafanarest.DualWriterMetrics, builderMetrics *builder.BuilderMetrics, + apiResourceConfig *serverstore.ResourceConfig, ) error { logger := logging.FromContext(ctx) for _, installer := range appInstallers { @@ -148,6 +144,7 @@ func InstallAPIs( dualWriteService: dualWriteService, dualWriterMetrics: dualWriterMetrics, builderMetrics: builderMetrics, + apiResourceConfig: apiResourceConfig, } if err := installer.InstallAPIs(wrapper, restOpsGetter); err != nil { return fmt.Errorf("failed to install APIs for app %s: %w", installer.ManifestData().AppName, err) diff --git a/pkg/services/apiserver/appinstaller/resourceconfig.go b/pkg/services/apiserver/appinstaller/resourceconfig.go new file mode 100644 index 00000000000..c2e45eb74e9 --- /dev/null +++ b/pkg/services/apiserver/appinstaller/resourceconfig.go @@ -0,0 +1,32 @@ +package appinstaller + +import ( + appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver" + "k8s.io/apimachinery/pkg/runtime/schema" + serverstorage "k8s.io/apiserver/pkg/server/storage" +) + +func NewAPIResourceConfig(installers []appsdkapiserver.AppInstaller) *serverstorage.ResourceConfig { + ret := serverstorage.NewResourceConfig() + enable := []schema.GroupVersion{} + disable := []schema.GroupVersion{} + + for _, installer := range installers { + for _, version := range installer.ManifestData().Versions { + gv := schema.GroupVersion{ + Group: installer.ManifestData().Group, + Version: version.Name, + } + if version.Served { + enable = append(enable, gv) + } else { + disable = append(disable, gv) + } + } + } + + ret.EnableVersions(enable...) + ret.DisableVersions(disable...) + + return ret +} diff --git a/pkg/services/apiserver/appinstaller/server.go b/pkg/services/apiserver/appinstaller/server.go index ea9b88e031e..20792eeab0d 100644 --- a/pkg/services/apiserver/appinstaller/server.go +++ b/pkg/services/apiserver/appinstaller/server.go @@ -10,6 +10,7 @@ import ( genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" genericrest "k8s.io/apiserver/pkg/registry/rest" genericapiserver "k8s.io/apiserver/pkg/server" + serverstorage "k8s.io/apiserver/pkg/server/storage" appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver" "github.com/grafana/grafana-app-sdk/logging" @@ -35,6 +36,7 @@ type serverWrapper struct { dualWriteService dualwrite.Service dualWriterMetrics *grafanarest.DualWriterMetrics builderMetrics *builder.BuilderMetrics + apiResourceConfig *serverstorage.ResourceConfig } func (s *serverWrapper) InstallAPIGroup(apiGroupInfo *genericapiserver.APIGroupInfo) error { @@ -50,6 +52,12 @@ func (s *serverWrapper) InstallAPIGroup(apiGroupInfo *genericapiserver.APIGroupI Group: s.installer.ManifestData().Group, Resource: resource, } + gvr := gr.WithVersion(v) + if s.apiResourceConfig != nil && !s.apiResourceConfig.ResourceEnabled(gvr) { + log.Debug("Skipping storage for disabled resource", "gvr", gvr.String(), "storagePath", storagePath) + delete(apiGroupInfo.VersionedResourcesStorageMap[v], storagePath) + continue + } storage := s.configureStorage(gr, dualWriteSupported, restStorage) if unifiedStorage, ok := storage.(grafanarest.Storage); ok && dualWriteSupported { log.Debug("Configuring dual writer for storage", "resource", gr.String(), "version", v, "storagePath", storagePath) diff --git a/pkg/services/apiserver/config.go b/pkg/services/apiserver/config.go index 8cf5789c777..dc2234e6e56 100644 --- a/pkg/services/apiserver/config.go +++ b/pkg/services/apiserver/config.go @@ -39,6 +39,13 @@ func applyGrafanaConfig(cfg *setting.Cfg, features featuremgmt.FeatureToggles, o apiserverCfg := cfg.SectionWithEnvOverrides("grafana-apiserver") + runtimeConfig := apiserverCfg.Key("runtime_config").String() + if runtimeConfig != "" { + if err := o.APIEnablementOptions.RuntimeConfig.Set(runtimeConfig); err != nil { + return fmt.Errorf("failed to set runtime config: %w", err) + } + } + o.RecommendedOptions.Etcd.StorageConfig.Transport.ServerList = apiserverCfg.Key("etcd_servers").Strings(",") o.RecommendedOptions.SecureServing.BindAddress = ip diff --git a/pkg/services/apiserver/options/options.go b/pkg/services/apiserver/options/options.go index a9209cdbc00..721f1ce570b 100644 --- a/pkg/services/apiserver/options/options.go +++ b/pkg/services/apiserver/options/options.go @@ -21,6 +21,7 @@ const defaultEtcdPathPrefix = "/registry/grafana.app" type Options struct { RecommendedOptions *genericoptions.RecommendedOptions + APIEnablementOptions *genericoptions.APIEnablementOptions GrafanaAggregatorOptions *GrafanaAggregatorOptions StorageOptions *StorageOptions ExtraOptions *ExtraOptions @@ -30,6 +31,7 @@ type Options struct { func NewOptions(codec runtime.Codec) *Options { return &Options{ RecommendedOptions: NewRecommendedOptions(codec), + APIEnablementOptions: genericoptions.NewAPIEnablementOptions(), GrafanaAggregatorOptions: NewGrafanaAggregatorOptions(), StorageOptions: NewStorageOptions(), ExtraOptions: NewExtraOptions(), @@ -38,6 +40,7 @@ func NewOptions(codec runtime.Codec) *Options { func (o *Options) AddFlags(fs *pflag.FlagSet) { o.RecommendedOptions.AddFlags(fs) + o.APIEnablementOptions.AddFlags(fs) o.GrafanaAggregatorOptions.AddFlags(fs) o.StorageOptions.AddFlags(fs) o.ExtraOptions.AddFlags(fs) diff --git a/pkg/services/apiserver/service.go b/pkg/services/apiserver/service.go index 58bf5ad4ac7..be580025823 100644 --- a/pkg/services/apiserver/service.go +++ b/pkg/services/apiserver/service.go @@ -304,11 +304,19 @@ func (s *service) start(ctx context.Context) error { return errs[0] } + if errs := o.APIEnablementOptions.Validate(s.scheme); len(errs) != 0 { + return errs[0] + } + serverConfig := genericapiserver.NewRecommendedConfig(s.codecs) if err := o.ApplyTo(serverConfig); err != nil { return err } + if err := o.APIEnablementOptions.ApplyTo(&serverConfig.Config, appinstaller.NewAPIResourceConfig(s.appInstallers), s.scheme); err != nil { + return err + } + serverConfig.Authorization.Authorizer = s.authorizer serverConfig.Authentication.Authenticator = authenticator.NewAuthenticator(serverConfig.Authentication.Authenticator) serverConfig.TracerProvider = s.tracing.GetTracerProvider() @@ -395,6 +403,7 @@ func (s *service) start(ctx context.Context) error { s.storageStatus, s.dualWriterMetrics, s.builderMetrics, + serverConfig.MergedResourceConfig, ); err != nil { return err } diff --git a/pkg/services/authn/clients/provisioning.go b/pkg/services/authn/clients/provisioning.go index d50a0ff600c..21031ea1031 100644 --- a/pkg/services/authn/clients/provisioning.go +++ b/pkg/services/authn/clients/provisioning.go @@ -7,7 +7,7 @@ import ( "time" claims "github.com/grafana/authlib/types" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/services/authn" ) diff --git a/pkg/services/authn/clients/provisioning_test.go b/pkg/services/authn/clients/provisioning_test.go index 065e24a7343..02afae68347 100644 --- a/pkg/services/authn/clients/provisioning_test.go +++ b/pkg/services/authn/clients/provisioning_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/grafana/authlib/types" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/services/authn" ) diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 9a0c468f0f1..ab2d9d14053 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -476,6 +476,13 @@ var ( Owner: grafanaAppPlatformSquad, FrontendOnly: true, }, + { + Name: "kubernetesShortURLs", + Description: "Routes short url requests from /api to the /apis endpoint", + Stage: FeatureStageExperimental, + Owner: grafanaAppPlatformSquad, + RequiresRestart: true, // changes the API routing + }, { Name: "dashboardDisableSchemaValidationV1", Description: "Disable schema validation for dashboards/v1", @@ -1890,6 +1897,13 @@ var ( Owner: grafanaDataProSquad, FrontendOnly: true, }, + { + Name: "newLogContext", + Description: "New Log Context component", + Stage: FeatureStageExperimental, + Owner: grafanaObservabilityLogsSquad, + FrontendOnly: true, + }, } ) diff --git a/pkg/services/featuremgmt/toggles-gitlog.csv b/pkg/services/featuremgmt/toggles-gitlog.csv index 4a0ff55bc6e..3024aa121f9 100644 --- a/pkg/services/featuremgmt/toggles-gitlog.csv +++ b/pkg/services/featuremgmt/toggles-gitlog.csv @@ -360,7 +360,6 @@ appPlatformAccessTokens,2024-09-05T16:18:44Z,2024-10-14T10:47:18Z,d5ebaa0ef92ede appSidecar,2024-09-09T12:45:05Z,2025-04-10T20:04:12Z,5e2ac24890906e5070323d87730dd78a4f885963,Andrej Ocenas vizActions,2024-09-09T14:11:55Z,2025-02-26T23:15:01Z,af48d3db1eb2d8681843f5997e50fea5e5ea3096,Adela Almasan groupAttributeSync,2024-09-09T15:29:43Z,,6ded6a8872204a818b3795dc733cc5fe5db066a0,Aaron Godin -kubernetesFolders,2024-09-10T09:22:08Z,2025-01-23T14:25:03Z,b12a29a1dac8b9aec4a99be08e1665939cb27dc5,Arati R. alertingFilterV2,2024-09-11T11:29:26Z,,90ee52e8d9c14237f8a57b622c0def7512e657cd,Gilles De Mey improvedExternalSessionHandling,2024-09-17T10:54:39Z,,41cd0f51800d4849345fc0980ca4173967fc8e9e,Misi datasourceAPIServers,2024-09-19T08:28:27Z,,f21a5987a22bcdb596d6a258d2960e4151348b63,Ryan McKinley @@ -374,7 +373,6 @@ grafanaAPIServerTestingWithExperimentalAPIs,2024-10-03T10:11:40Z,2025-01-23T14:2 pluginsSriChecks,2024-10-04T12:55:09Z,,0db65d229e36b78802c1e8bd0713ac44e7a7cdc7,Giuseppe Guerra onPremToCloudMigrationsAlerts,2024-10-07T10:53:24Z,2024-12-17T11:56:18Z,712314e8324fd86ec33ecb840f868eb1f1cac154,Matheus Macabu appPlatformGrpcClientAuth,2024-10-14T10:47:18Z,,a69ee676babc7644da41781efc5ae2c301f06de6,Claudiu Dragalina-Paraipan -kubernetesDashboardsAPI,2024-10-15T19:30:05Z,2024-12-10T18:35:36Z,644a16048f034f6bc79883678f1f1a4b48233483,Stephanie Hingtgen unifiedStorageBigObjectsSupport,2024-10-17T10:18:29Z,,3457f219be1c8bce99f713d7a907ee339ef38229,Ryan McKinley timeRangeProvider,2024-10-22T10:52:33Z,,3bf3290340a7842bb1d83647343234b2e9e83b18,Andrej Ocenas dashboardNewLayouts,2024-10-23T08:55:45Z,,b700de81224caacd327fafb6ce0dfda8b38d39c6,Torkel Ödegaard @@ -407,7 +405,6 @@ feedbackButton,2024-12-02T17:08:15Z,,8a1b89a5ebb847f6b29e92dcea796f00c30431d6,Mi elasticsearchCrossClusterSearch,2024-12-12T22:20:04Z,,b3a12f486eba69e20dd7ff3a3d4dd065ede7a99f,Isabella Siu unifiedHistory,2024-12-13T10:41:18Z,,aac62c89dae1092836a91d1b6ae6bd7127fe676a,Laura Fernández lokiLabelNamesQueryApi,2024-12-13T14:31:41Z,,5ac7443fcec0db412d3333044a82c2c26b5aece7,Sven Grossmann -kubernetesCliDashboards,2024-12-13T22:55:43Z,2025-02-18T23:11:26Z,8f6e9f8ed0a5024a510cc337c9f1e6972bfb23d4,Stephanie Hingtgen useV2DashboardsAPI,2024-12-17T21:17:09Z,2025-03-12T17:43:32Z,070f0e4457c5967102ef157197073dc2662f6fb8,Dominik Prokop investigationsBackend,2024-12-18T08:31:03Z,,f46c07aba7b6faccd2ecafc83051d1410cacc867,Jackson Coelho unifiedStorageSearchSprinkles,2024-12-18T17:00:54Z,,4837585cab0fd84184a8c6f5d6891f442a2b95f1,owensmallwood @@ -421,7 +418,6 @@ improvedExternalSessionHandlingSAML,2025-01-09T17:02:49Z,,c52ec21c75ab72c2f7d282 teamHttpHeadersMimir,2025-01-13T10:42:47Z,,04acbcdef23f673bd6bbfdbbece29c9769ce155a,Eric Leijonmarck ABTestFeatureToggleA,2025-01-13T21:13:13Z,2025-05-27T19:18:23Z,009d7f42b3d09b3a6be1f00f07314e2b25af7ebc,Nathan Marrs ABTestFeatureToggleB,2025-01-13T21:13:13Z,2025-05-27T19:18:23Z,009d7f42b3d09b3a6be1f00f07314e2b25af7ebc,Nathan Marrs -kubernetesFoldersServiceV2,2025-01-13T21:15:35Z,2025-02-18T23:11:26Z,766d645d827f5e6e0872ae30e5fe23226ae85785,maicon queryLibraryDashboards,2025-01-14T11:01:15Z,2025-02-14T16:39:22Z,740cd22fe51a3543c182857f31fc97fd42263306,Ashley Harrison elasticsearchImprovedParsing,2025-01-15T17:05:54Z,,bab55a4cb84f2ba57838f96a492ab9aa7f307957,Adam Yeats grafanaAdvisor,2025-01-20T10:08:00Z,,c1364d6be6f552203ba786f17a89664304b89247,Andres Martinez Gotor diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 14f65065ed9..a0319c94bfb 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -61,6 +61,7 @@ formatString,GA,@grafana/dataviz-squad,false,false,true kubernetesSnapshots,experimental,@grafana/grafana-app-platform-squad,false,true,false kubernetesLibraryPanels,experimental,@grafana/grafana-app-platform-squad,false,true,false kubernetesDashboards,experimental,@grafana/grafana-app-platform-squad,false,false,true +kubernetesShortURLs,experimental,@grafana/grafana-app-platform-squad,false,true,false dashboardDisableSchemaValidationV1,experimental,@grafana/grafana-app-platform-squad,false,false,false dashboardDisableSchemaValidationV2,experimental,@grafana/grafana-app-platform-squad,false,false,false dashboardSchemaValidationLogging,experimental,@grafana/grafana-app-platform-squad,false,false,false @@ -244,3 +245,4 @@ dashboardDsAdHocFiltering,experimental,@grafana/datapro,false,false,true dashboardLevelTimeMacros,experimental,@grafana/dashboards-squad,false,false,true alertmanagerRemoteSecondaryWithRemoteState,experimental,@grafana/alerting-squad,false,false,false adhocFiltersInTooltips,experimental,@grafana/datapro,false,false,true +newLogContext,experimental,@grafana/observability-logs,false,false,true diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index c74ce4befc4..c45b269451b 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -255,6 +255,10 @@ const ( // Use the kubernetes API in the frontend for dashboards FlagKubernetesDashboards = "kubernetesDashboards" + // FlagKubernetesShortURLs + // Routes short url requests from /api to the /apis endpoint + FlagKubernetesShortURLs = "kubernetesShortURLs" + // FlagDashboardDisableSchemaValidationV1 // Disable schema validation for dashboards/v1 FlagDashboardDisableSchemaValidationV1 = "dashboardDisableSchemaValidationV1" @@ -986,4 +990,8 @@ const ( // FlagAdhocFiltersInTooltips // Enable adhoc filter buttons in visualization tooltips FlagAdhocFiltersInTooltips = "adhocFiltersInTooltips" + + // FlagNewLogContext + // New Log Context component + FlagNewLogContext = "newLogContext" ) diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 2324d34c0f8..503f8a657f6 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -1782,6 +1782,19 @@ "requiresRestart": true } }, + { + "metadata": { + "name": "kubernetesShortURLs", + "resourceVersion": "1753722806283", + "creationTimestamp": "2025-07-28T17:13:26Z" + }, + "spec": { + "description": "Routes short url requests from /api to the /apis endpoint", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresRestart": true + } + }, { "metadata": { "name": "kubernetesSnapshots", @@ -2150,6 +2163,19 @@ "expression": "false" } }, + { + "metadata": { + "name": "newLogContext", + "resourceVersion": "1754044501326", + "creationTimestamp": "2025-08-01T10:35:01Z" + }, + "spec": { + "description": "New Log Context component", + "stage": "experimental", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, { "metadata": { "name": "newLogsPanel", diff --git a/pkg/services/live/features/watch.go b/pkg/services/live/features/watch.go index 05a7f875518..51e6253ac17 100644 --- a/pkg/services/live/features/watch.go +++ b/pkg/services/live/features/watch.go @@ -11,7 +11,7 @@ import ( "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/dynamic" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/authlib/types" "github.com/grafana/grafana-app-sdk/logging" diff --git a/pkg/services/mtdsclient/mt_datasource_client_builder.go b/pkg/services/mtdsclient/mt_datasource_client_builder.go index d5108aef4e2..3c964aeefd1 100644 --- a/pkg/services/mtdsclient/mt_datasource_client_builder.go +++ b/pkg/services/mtdsclient/mt_datasource_client_builder.go @@ -23,23 +23,21 @@ func NewNullMTDatasourceClientBuilder() MTDatasourceClientBuilder { return &nullBuilder{} } -type MtDatasourceClientBuilderWithClientSupplier struct { - clientSupplier clientapi.DataSourceClientSupplier - ctx context.Context - headers map[string]string - instanceConfig clientapi.InstanceConfigurationSettings - logger log.Logger +type MtDatasourceClientBuilderWithInstance struct { + instance clientapi.Instance + ctx context.Context + headers map[string]string + logger log.Logger } -func (b *MtDatasourceClientBuilderWithClientSupplier) BuildClient(pluginId string, uid string) (clientapi.QueryDataClient, bool) { - dsClient, err := b.clientSupplier.GetDataSourceClient( +func (b *MtDatasourceClientBuilderWithInstance) BuildClient(pluginId string, uid string) (clientapi.QueryDataClient, bool) { + dsClient, err := b.instance.GetDataSourceClient( b.ctx, v0alpha1.DataSourceRef{ Type: pluginId, UID: uid, }, b.headers, - b.instanceConfig, ) if err != nil { b.logger.Debug("failed to get mt ds client", "error", err) @@ -48,20 +46,18 @@ func (b *MtDatasourceClientBuilderWithClientSupplier) BuildClient(pluginId strin return dsClient, true } -// TODO: I think we might be able to refactor this to just use the client supplier directly -func NewMtDatasourceClientBuilderWithClientSupplier( - clientSupplier clientapi.DataSourceClientSupplier, +// TODO: I think we might be able to refactor this to just use the instance +func NewMtDatasourceClientBuilderWithInstance( + instance clientapi.Instance, ctx context.Context, headers map[string]string, - instanceConfig clientapi.InstanceConfigurationSettings, logger log.Logger, ) MTDatasourceClientBuilder { - return &MtDatasourceClientBuilderWithClientSupplier{ - clientSupplier: clientSupplier, - ctx: ctx, - headers: headers, - instanceConfig: instanceConfig, - logger: logger, + return &MtDatasourceClientBuilderWithInstance{ + instance: instance, + ctx: ctx, + headers: headers, + logger: logger, } } diff --git a/pkg/services/navtree/models.go b/pkg/services/navtree/models.go index 5514705ed9a..250b3bf8717 100644 --- a/pkg/services/navtree/models.go +++ b/pkg/services/navtree/models.go @@ -17,6 +17,7 @@ const ( WeightDashboard WeightExplore WeightDrilldown + WeightAssistant WeightAlerting WeightAlertsAndIncidents WeightAIAndML diff --git a/pkg/services/navtree/navtreeimpl/applinks.go b/pkg/services/navtree/navtreeimpl/applinks.go index 42770b6ee96..b4571c88a69 100644 --- a/pkg/services/navtree/navtreeimpl/applinks.go +++ b/pkg/services/navtree/navtreeimpl/applinks.go @@ -324,7 +324,8 @@ func (s *ServiceImpl) readNavigationSettings() { "grafana-irm-app": {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 3, Text: "IRM"}, "grafana-oncall-app": {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 4, Text: "OnCall"}, "grafana-incident-app": {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 5, Text: "Incident"}, - "grafana-ml-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightAIAndML, Text: "AI & machine learning", SubTitle: "Explore AI and machine learning features", Icon: "gf-ml-alt"}, + "grafana-assistant-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightAssistant, Text: "Assistant", SubTitle: "AI-powered assistant for Grafana", Icon: "ai-sparkle", IsNew: true}, + "grafana-ml-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightAIAndML, Text: "Machine Learning", SubTitle: "Explore AI and machine learning features", Icon: "gf-ml-alt"}, "grafana-slo-app": {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 7}, "grafana-cloud-link-app": {SectionID: navtree.NavIDCfgPlugins, SortWeight: 3}, "grafana-costmanagementui-app": {SectionID: navtree.NavIDCfg, Text: "Cost management"}, diff --git a/pkg/services/ngalert/api/api_convert_prometheus.go b/pkg/services/ngalert/api/api_convert_prometheus.go index 345569be3c8..1d4fd9ac814 100644 --- a/pkg/services/ngalert/api/api_convert_prometheus.go +++ b/pkg/services/ngalert/api/api_convert_prometheus.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "mime" "net/http" "path/filepath" "strconv" @@ -55,6 +56,10 @@ const ( // The value should be comma-separated key=value pairs, e.g., "environment=production,team=alerting". mergeMatchersHeader = "X-Grafana-Alerting-Merge-Matchers" + // extraLabelsHeader is the header that specifies extra labels to be added to all imported rules. + // The value should be comma-separated key=value pairs, e.g., "environment=production,team=alerting". + extraLabelsHeader = "X-Grafana-Alerting-Extra-Labels" + // configIdentifierHeader is the header that specifies the identifier for imported Alertmanager config. configIdentifierHeader = "X-Grafana-Alerting-Config-Identifier" defaultConfigIdentifier = "default" @@ -155,7 +160,7 @@ func NewConvertPrometheusSrv( // RouteConvertPrometheusGetRules returns all Grafana-managed alert rules in all namespaces (folders) // that were imported from a Prometheus-compatible source. -// It responds with a YAML containing a mapping of folders to arrays of Prometheus rule groups. +// It responds with JSON or YAML containing a mapping of folders to arrays of Prometheus rule groups. func (srv *ConvertPrometheusSrv) RouteConvertPrometheusGetRules(c *contextmodel.ReqContext) response.Response { logger := srv.logger.FromContext(c.Req.Context()) @@ -166,7 +171,7 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusGetRules(c *contextmodel. if len(folders) == 0 || errors.Is(err, dashboards.ErrFolderNotFound) { // If there is no such folder or no children, return empty response // because mimirtool expects 200 OK response in this case. - return response.YAML(http.StatusOK, map[string][]apimodels.PrometheusRuleGroup{}) + return convertPrometheusResponse(c, http.StatusOK, map[string][]apimodels.PrometheusRuleGroup{}) } if err != nil { logger.Error("Failed to get folders", "error", err) @@ -193,7 +198,7 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusGetRules(c *contextmodel. return errorToResponse(err) } - return response.YAML(http.StatusOK, namespaces) + return convertPrometheusResponse(c, http.StatusOK, namespaces) } // RouteConvertPrometheusDeleteNamespace deletes all rule groups that were imported from a Prometheus-compatible source @@ -256,7 +261,7 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusDeleteRuleGroup(c *contex } // RouteConvertPrometheusGetNamespace returns the Grafana-managed alert rules for a specified namespace (folder). -// It responds with a YAML containing a mapping of a single folder to an array of Prometheus rule groups. +// It responds with JSON or YAML containing a mapping of a single folder to an array of Prometheus rule groups. func (srv *ConvertPrometheusSrv) RouteConvertPrometheusGetNamespace(c *contextmodel.ReqContext, namespaceTitle string) response.Response { logger := srv.logger.FromContext(c.Req.Context()) @@ -286,11 +291,11 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusGetNamespace(c *contextmo return errorToResponse(err) } - return response.YAML(http.StatusOK, ns) + return convertPrometheusResponse(c, http.StatusOK, ns) } // RouteConvertPrometheusGetRuleGroup retrieves a single rule group for a given namespace (folder) -// in Prometheus-compatible YAML format if it was imported from a Prometheus-compatible source. +// in Prometheus-compatible JSON or YAML format if it was imported from a Prometheus-compatible source. func (srv *ConvertPrometheusSrv) RouteConvertPrometheusGetRuleGroup(c *contextmodel.ReqContext, namespaceTitle string, group string) response.Response { logger := srv.logger.FromContext(c.Req.Context()) @@ -332,7 +337,7 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusGetRuleGroup(c *contextmo return errorToResponse(err) } - return response.YAML(http.StatusOK, promGroup) + return convertPrometheusResponse(c, http.StatusOK, promGroup) } // RouteConvertPrometheusPostRuleGroup converts a Prometheus rule group into a Grafana rule group @@ -396,6 +401,12 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusPostRuleGroups(c *context return errorToResponse(err) } + extraLabels, err := parseExtraLabelsHeader(c) + if err != nil { + logger.Error("Failed to parse extra labels header", "error", err) + return errorToResponse(err) + } + // 2. Convert Prometheus Rules to GMA grafanaGroups := make([]*models.AlertRuleGroup, 0, len(promNamespaces)) for ns, rgs := range promNamespaces { @@ -426,6 +437,7 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusPostRuleGroups(c *context pauseAlertRules, keepOriginalRuleDefinition, notificationSettings, + extraLabels, logger, ) if err != nil { @@ -476,6 +488,7 @@ func (srv *ConvertPrometheusSrv) convertToGrafanaRuleGroup( pauseAlertRules bool, keepOriginalRuleDefinition bool, notificationSettings []models.NotificationSettings, + extraLabels map[string]string, logger log.Logger, ) (*models.AlertRuleGroup, error) { logger.Info("Converting Prometheus rules to Grafana rules", "rules", len(promGroup.Rules), "folder_uid", namespaceUID, "datasource_uid", ds.UID, "datasource_type", ds.Type) @@ -517,6 +530,7 @@ func (srv *ConvertPrometheusSrv) convertToGrafanaRuleGroup( KeepOriginalRuleDefinition: util.Pointer(keepOriginalRuleDefinition), EvaluationOffset: &srv.cfg.PrometheusConversion.RuleQueryOffset, NotificationSettings: notificationSettings, + ExtraLabels: extraLabels, }, ) if err != nil { @@ -608,7 +622,7 @@ func (srv *ConvertPrometheusSrv) RouteConvertPrometheusGetAlertmanagerConfig(c * TemplateFiles: extraCfg.TemplateFiles, } - resp := response.YAML(http.StatusOK, respBody) + resp := convertPrometheusResponse(c, http.StatusOK, respBody) resp.SetHeader(configIdentifierHeader, extraCfg.Identifier) resp.SetHeader(mergeMatchersHeader, formatMergeMatchers(extraCfg.MergeMatchers)) @@ -757,6 +771,35 @@ func parseNotificationSettingsHeader(ctx *contextmodel.ReqContext) ([]models.Not return notificationSettings, nil } +// parseKeyValuePairs parses a comma-separated list of key=value pairs. +// Expected format: "key1=value1,key2=value2" +func parseKeyValuePairs(input string, headerName string) (map[string]string, error) { + input = strings.TrimSpace(input) + if input == "" { + return nil, nil + } + + result := make(map[string]string) + + for pair := range strings.SplitSeq(input, ",") { + parts := strings.SplitN(strings.TrimSpace(pair), "=", 2) + if len(parts) != 2 { + return nil, errInvalidHeaderValue(headerName, errors.New("format should be 'key=value,key2=value2'")) + } + + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + + if key == "" || value == "" { + return nil, errInvalidHeaderValue(headerName, errors.New("keys and values cannot be empty")) + } + + result[key] = value + } + + return result, nil +} + // parseMergeMatchersHeader parses the merge matchers header value. // Expected format: "key1=value1,key2=value2" func parseMergeMatchersHeader(c *contextmodel.ReqContext) (amconfig.Matchers, error) { @@ -766,21 +809,13 @@ func parseMergeMatchersHeader(c *contextmodel.ReqContext) (amconfig.Matchers, er return amconfig.Matchers{}, errInvalidHeaderValue(mergeMatchersHeader, errors.New("value cannot be empty")) } + kvPairs, err := parseKeyValuePairs(matchersStr, mergeMatchersHeader) + if err != nil { + return nil, err + } + matchers := amconfig.Matchers{} - - for pair := range strings.SplitSeq(matchersStr, ",") { - parts := strings.SplitN(strings.TrimSpace(pair), "=", 2) - if len(parts) != 2 { - return nil, errInvalidHeaderValue(mergeMatchersHeader, errors.New("format should be 'key=value,key2=value2'")) - } - - key := strings.TrimSpace(parts[0]) - value := strings.TrimSpace(parts[1]) - - if key == "" || value == "" { - return nil, errInvalidHeaderValue(mergeMatchersHeader, errors.New("keys and values cannot be empty")) - } - + for key, value := range kvPairs { matchers = append(matchers, &labels.Matcher{ Type: labels.MatchEqual, Name: key, @@ -791,6 +826,13 @@ func parseMergeMatchersHeader(c *contextmodel.ReqContext) (amconfig.Matchers, er return matchers, nil } +// parseExtraLabelsHeader parses the extra labels header value. +// Expected format: "key1=value1,key2=value2" +func parseExtraLabelsHeader(c *contextmodel.ReqContext) (map[string]string, error) { + labelsStr := strings.TrimSpace(c.Req.Header.Get(extraLabelsHeader)) + return parseKeyValuePairs(labelsStr, extraLabelsHeader) +} + func formatMergeMatchers(matchers amconfig.Matchers) string { var pairs []string for _, matcher := range matchers { @@ -808,3 +850,22 @@ func parseConfigIdentifierHeader(c *contextmodel.ReqContext) string { } return identifier } + +// convertPrometheusResponse returns a JSON or YAML response based on the Accept header. +// Default is YAML for backward compatibility with mimirtool. +func convertPrometheusResponse(c *contextmodel.ReqContext, status int, body interface{}) *response.NormalResponse { + acceptHeader := c.Req.Header.Get("Accept") + + for _, accept := range strings.Split(acceptHeader, ",") { + mediaType, _, err := mime.ParseMediaType(accept) + if err != nil { + continue + } + + if mediaType == "application/json" { + return response.JSON(status, body) + } + } + + return response.YAML(status, body) +} diff --git a/pkg/services/ngalert/api/api_convert_prometheus_test.go b/pkg/services/ngalert/api/api_convert_prometheus_test.go index ea3a9a16a13..44753418cce 100644 --- a/pkg/services/ngalert/api/api_convert_prometheus_test.go +++ b/pkg/services/ngalert/api/api_convert_prometheus_test.go @@ -6,6 +6,7 @@ import ( "errors" "net/http" "net/http/httptest" + "strings" "testing" "time" @@ -291,6 +292,91 @@ func TestRouteConvertPrometheusPostRuleGroup(t *testing.T) { } }) + t.Run("with extra labels header should apply labels to all rules", func(t *testing.T) { + srv, _, ruleStore := createConvertPrometheusSrv(t) + rc := createRequestCtx() + rc.Req.Header.Set(extraLabelsHeader, "environment=production,team=alerting") + + response := srv.RouteConvertPrometheusPostRuleGroup(rc, "test", simpleGroup) + require.Equal(t, http.StatusAccepted, response.Status()) + + rules, err := ruleStore.ListAlertRules(context.Background(), &models.ListAlertRulesQuery{ + OrgID: 1, + }) + require.NoError(t, err) + require.Len(t, rules, 2) + + for _, rule := range rules { + require.Equal(t, "production", rule.Labels["environment"]) + require.Equal(t, "alerting", rule.Labels["team"]) + } + + // Original labels must be preserved + alertRule := rules[0] + if alertRule.Title == "recorded-metric" { + alertRule = rules[1] + } + require.Equal(t, "critical", alertRule.Labels["severity"]) + }) + + t.Run("with extra labels that conflict with rule labels", func(t *testing.T) { + srv, _, ruleStore := createConvertPrometheusSrv(t) + rc := createRequestCtx() + // rules in the simpleGroup already have a severity label, so + // it should not be overwritten by the label from the header + rc.Req.Header.Set(extraLabelsHeader, "environment=production,severity=low") + + response := srv.RouteConvertPrometheusPostRuleGroup(rc, "test", simpleGroup) + require.Equal(t, http.StatusAccepted, response.Status()) + + rules, err := ruleStore.ListAlertRules(context.Background(), &models.ListAlertRulesQuery{ + OrgID: 1, + }) + require.NoError(t, err) + require.Len(t, rules, 2) + + for _, rule := range rules { + require.Equal(t, "production", rule.Labels["environment"]) + require.NotEqual(t, "low", rule.Labels["severity"]) + } + }) + + t.Run("with invalid extra labels header should return 400", func(t *testing.T) { + testCases := []struct { + name string + headerValue string + expectedError string + }{ + { + name: "missing equals sign", + headerValue: "environment,team=platform", + expectedError: "Invalid value for header X-Grafana-Alerting-Extra-Labels: format should be 'key=value,key2=value2'", + }, + { + name: "empty key", + headerValue: "=production,team=platform", + expectedError: "Invalid value for header X-Grafana-Alerting-Extra-Labels: keys and values cannot be empty", + }, + { + name: "empty value", + headerValue: "environment=,team=platform", + expectedError: "Invalid value for header X-Grafana-Alerting-Extra-Labels: keys and values cannot be empty", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + srv, _, _ := createConvertPrometheusSrv(t) + rc := createRequestCtx() + rc.Req.Header.Set(extraLabelsHeader, tc.headerValue) + + response := srv.RouteConvertPrometheusPostRuleGroup(rc, "test", simpleGroup) + require.Equal(t, http.StatusBadRequest, response.Status()) + require.Contains(t, string(response.Body()), tc.expectedError) + }) + } + }) + t.Run("with empty rule group name should return 400", func(t *testing.T) { srv, _, _ := createConvertPrometheusSrv(t) rc := createRequestCtx() @@ -597,17 +683,34 @@ func TestRouteConvertPrometheusGetRuleGroup(t *testing.T) { GenerateRef() ruleStore.PutRule(context.Background(), ruleInOtherFolder) - getResp := srv.RouteConvertPrometheusGetRuleGroup(rc, fldr.Title, groupKey.RuleGroup) - require.Equal(t, http.StatusOK, getResp.Status()) + t.Run("YAML response", func(t *testing.T) { + getResp := srv.RouteConvertPrometheusGetRuleGroup(rc, fldr.Title, groupKey.RuleGroup) + require.Equal(t, http.StatusOK, getResp.Status()) - var respGroup apimodels.PrometheusRuleGroup - err := yaml.Unmarshal(getResp.Body(), &respGroup) - require.NoError(t, err) + var respGroup apimodels.PrometheusRuleGroup + err := yaml.Unmarshal(getResp.Body(), &respGroup) + require.NoError(t, err) - require.Equal(t, groupKey.RuleGroup, respGroup.Name) - require.Equal(t, prommodel.Duration(time.Duration(rule.IntervalSeconds)*time.Second), respGroup.Interval) - require.Len(t, respGroup.Rules, 1) - require.Equal(t, promRule.Alert, respGroup.Rules[0].Alert) + require.Equal(t, groupKey.RuleGroup, respGroup.Name) + require.Equal(t, prommodel.Duration(time.Duration(rule.IntervalSeconds)*time.Second), respGroup.Interval) + require.Len(t, respGroup.Rules, 1) + require.Equal(t, promRule.Alert, respGroup.Rules[0].Alert) + }) + + t.Run("JSON response", func(t *testing.T) { + rc.Req.Header.Set("Accept", "application/json") + getResp := srv.RouteConvertPrometheusGetRuleGroup(rc, fldr.Title, groupKey.RuleGroup) + require.Equal(t, http.StatusOK, getResp.Status()) + + var jsonGroup apimodels.PrometheusRuleGroup + err := json.Unmarshal(getResp.Body(), &jsonGroup) + require.NoError(t, err) + + require.Equal(t, groupKey.RuleGroup, jsonGroup.Name) + require.Equal(t, prommodel.Duration(time.Duration(rule.IntervalSeconds)*time.Second), jsonGroup.Interval) + require.Len(t, jsonGroup.Rules, 1) + require.Equal(t, promRule.Alert, jsonGroup.Rules[0].Alert) + }) }) } @@ -690,16 +793,32 @@ func TestRouteConvertPrometheusGetNamespace(t *testing.T) { ruleStore.PutRule(context.Background(), rule) } - response := srv.RouteConvertPrometheusGetNamespace(rc, fldr.Title) - require.Equal(t, http.StatusOK, response.Status()) + t.Run("YAML response", func(t *testing.T) { + response := srv.RouteConvertPrometheusGetNamespace(rc, fldr.Title) + require.Equal(t, http.StatusOK, response.Status()) - var respNamespaces map[string][]apimodels.PrometheusRuleGroup - err := yaml.Unmarshal(response.Body(), &respNamespaces) - require.NoError(t, err) + var respNamespaces map[string][]apimodels.PrometheusRuleGroup + err := yaml.Unmarshal(response.Body(), &respNamespaces) + require.NoError(t, err) - require.Len(t, respNamespaces, 1) - require.Contains(t, respNamespaces, fldr.Title) - require.ElementsMatch(t, respNamespaces[fldr.Title], []apimodels.PrometheusRuleGroup{promGroup1, promGroup2}) + require.Len(t, respNamespaces, 1) + require.Contains(t, respNamespaces, fldr.Title) + require.ElementsMatch(t, respNamespaces[fldr.Title], []apimodels.PrometheusRuleGroup{promGroup1, promGroup2}) + }) + + t.Run("JSON response", func(t *testing.T) { + rc.Req.Header.Set("Accept", "application/json") + response := srv.RouteConvertPrometheusGetNamespace(rc, fldr.Title) + require.Equal(t, http.StatusOK, response.Status()) + + var jsonNamespaces map[string][]apimodels.PrometheusRuleGroup + err := json.Unmarshal(response.Body(), &jsonNamespaces) + require.NoError(t, err) + + require.Len(t, jsonNamespaces, 1) + require.Contains(t, jsonNamespaces, fldr.Title) + require.ElementsMatch(t, jsonNamespaces[fldr.Title], []apimodels.PrometheusRuleGroup{promGroup1, promGroup2}) + }) }) } @@ -821,19 +940,120 @@ func TestRouteConvertPrometheusGetRules(t *testing.T) { ruleStore.PutRule(context.Background(), rule) } - response := srv.RouteConvertPrometheusGetRules(rc) - require.Equal(t, http.StatusOK, response.Status()) + t.Run("YAML response", func(t *testing.T) { + response := srv.RouteConvertPrometheusGetRules(rc) + require.Equal(t, http.StatusOK, response.Status()) - var respNamespaces map[string][]apimodels.PrometheusRuleGroup - err := yaml.Unmarshal(response.Body(), &respNamespaces) - require.NoError(t, err) + var respNamespaces map[string][]apimodels.PrometheusRuleGroup + err := yaml.Unmarshal(response.Body(), &respNamespaces) + require.NoError(t, err) - require.Len(t, respNamespaces, 1) - require.Contains(t, respNamespaces, fldr.Title) - require.ElementsMatch(t, respNamespaces[fldr.Title], []apimodels.PrometheusRuleGroup{promGroup1, promGroup2}) + require.Len(t, respNamespaces, 1) + require.Contains(t, respNamespaces, fldr.Title) + require.ElementsMatch(t, respNamespaces[fldr.Title], []apimodels.PrometheusRuleGroup{promGroup1, promGroup2}) + }) + + t.Run("JSON response", func(t *testing.T) { + rc.Req.Header.Set("Accept", "application/json") + response := srv.RouteConvertPrometheusGetRules(rc) + require.Equal(t, http.StatusOK, response.Status()) + + var jsonNamespaces map[string][]apimodels.PrometheusRuleGroup + err := json.Unmarshal(response.Body(), &jsonNamespaces) + require.NoError(t, err) + + require.Len(t, jsonNamespaces, 1) + require.Contains(t, jsonNamespaces, fldr.Title) + require.ElementsMatch(t, jsonNamespaces[fldr.Title], []apimodels.PrometheusRuleGroup{promGroup1, promGroup2}) + }) }) } +func TestConvertPrometheusResponse(t *testing.T) { + testData := map[string][]apimodels.PrometheusRuleGroup{ + "test": { + { + Name: "test-group", + Rules: []apimodels.PrometheusRule{ + { + Alert: "TestAlert", + Expr: "up == 0", + }, + }, + }, + }, + } + + testCases := []struct { + name string + acceptHeader string + expectedType string + checkResponse func(t *testing.T, body []byte) + }{ + { + name: "by default returns YAML", + expectedType: "text/yaml", + checkResponse: func(t *testing.T, body []byte) { + require.True(t, strings.Contains(string(body), "test-group")) + require.True(t, strings.Contains(string(body), "TestAlert")) + var result map[string][]apimodels.PrometheusRuleGroup + err := yaml.Unmarshal(body, &result) + require.NoError(t, err) + }, + }, + { + name: "with application/json Accept header returns JSON", + acceptHeader: "application/json", + expectedType: "application/json", + checkResponse: func(t *testing.T, body []byte) { + require.True(t, strings.Contains(string(body), "test-group")) + require.True(t, strings.Contains(string(body), "TestAlert")) + var result map[string][]apimodels.PrometheusRuleGroup + err := json.Unmarshal(body, &result) + require.NoError(t, err) + }, + }, + { + name: "with application/yaml accept header returns YAML", + acceptHeader: "application/yaml", + expectedType: "text/yaml", + checkResponse: func(t *testing.T, body []byte) { + require.True(t, strings.Contains(string(body), "test-group")) + require.True(t, strings.Contains(string(body), "TestAlert")) + var result map[string][]apimodels.PrometheusRuleGroup + err := yaml.Unmarshal(body, &result) + require.NoError(t, err) + }, + }, + { + name: "with a header with both json and yaml returns JSON", + acceptHeader: "application/yaml, application/json", + expectedType: "application/json", + checkResponse: func(t *testing.T, body []byte) { + require.True(t, strings.Contains(string(body), "test-group")) + require.True(t, strings.Contains(string(body), "TestAlert")) + var result map[string][]apimodels.PrometheusRuleGroup + err := json.Unmarshal(body, &result) + require.NoError(t, err) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rc := createRequestCtx() + if tc.acceptHeader != "" { + rc.Req.Header.Set("Accept", tc.acceptHeader) + } + + response := convertPrometheusResponse(rc, http.StatusOK, testData) + + require.Equal(t, http.StatusOK, response.Status()) + tc.checkResponse(t, response.Body()) + }) + } +} + func TestRouteConvertPrometheusDeleteNamespace(t *testing.T) { t.Run("for non-existent folder should return 404", func(t *testing.T) { srv, _, _ := createConvertPrometheusSrv(t) diff --git a/pkg/services/ngalert/api/persist.go b/pkg/services/ngalert/api/persist.go index 2150edbe34c..5a60c2e0637 100644 --- a/pkg/services/ngalert/api/persist.go +++ b/pkg/services/ngalert/api/persist.go @@ -23,6 +23,7 @@ type RuleStore interface { GetAlertRuleByUID(ctx context.Context, query *ngmodels.GetAlertRuleByUIDQuery) (*ngmodels.AlertRule, error) GetAlertRulesGroupByRuleUID(ctx context.Context, query *ngmodels.GetAlertRulesGroupByRuleUIDQuery) ([]*ngmodels.AlertRule, error) ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (ngmodels.RulesGroup, error) + ListAlertRulesByGroup(ctx context.Context, query *ngmodels.ListAlertRulesByGroupQuery) (ngmodels.RulesGroup, string, error) ListDeletedRules(ctx context.Context, orgID int64) ([]*ngmodels.AlertRule, error) // InsertAlertRules will insert all alert rules passed into the function diff --git a/pkg/services/ngalert/api/prometheus/api_prometheus.go b/pkg/services/ngalert/api/prometheus/api_prometheus.go index 2e753a6db1d..c223bce9aac 100644 --- a/pkg/services/ngalert/api/prometheus/api_prometheus.go +++ b/pkg/services/ngalert/api/prometheus/api_prometheus.go @@ -2,7 +2,6 @@ package api import ( "context" - "encoding/base64" "encoding/json" "errors" "fmt" @@ -30,7 +29,7 @@ import ( type RuleStoreReader interface { GetUserVisibleNamespaces(context.Context, int64, identity.Requester) (map[string]*folder.Folder, error) - ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (ngmodels.RulesGroup, error) + ListAlertRulesStore } type RuleGroupAccessControlService interface { @@ -241,7 +240,7 @@ type RuleGroupStatusesOptions struct { } type ListAlertRulesStore interface { - ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (ngmodels.RulesGroup, error) + ListAlertRulesByGroup(ctx context.Context, query *ngmodels.ListAlertRulesByGroupQuery) (ngmodels.RulesGroup, string, error) } func (srv PrometheusSrv) RouteGetRuleStatuses(c *contextmodel.ReqContext) response.Response { @@ -476,15 +475,24 @@ func PrepareRuleGroupStatuses(log log.Logger, store ListAlertRulesStore, opts Ru receiverName := opts.Query.Get("receiver_name") - alertRuleQuery := ngmodels.ListAlertRulesQuery{ - OrgID: opts.OrgID, - NamespaceUIDs: namespaceUIDs, - DashboardUID: dashboardUID, - PanelID: panelID, - RuleGroups: ruleGroups, - ReceiverName: receiverName, + maxGroups := getInt64WithDefault(opts.Query, "group_limit", -1) + nextToken := opts.Query.Get("group_next_token") + + if maxGroups == 0 { + return ruleResponse } - ruleList, err := store.ListAlertRules(opts.Ctx, &alertRuleQuery) + + byGroupQuery := ngmodels.ListAlertRulesByGroupQuery{ + OrgID: opts.OrgID, + GroupLimit: maxGroups, + GroupContinueToken: nextToken, + NamespaceUIDs: namespaceUIDs, + DashboardUID: dashboardUID, + PanelID: panelID, + RuleGroups: ruleGroups, + ReceiverName: receiverName, + } + ruleList, continueToken, err := store.ListAlertRulesByGroup(opts.Ctx, &byGroupQuery) if err != nil { ruleResponse.Status = "error" ruleResponse.Error = fmt.Sprintf("failure getting rules: %s", err.Error()) @@ -498,31 +506,9 @@ func PrepareRuleGroupStatuses(log log.Logger, store ListAlertRulesStore, opts Ru ruleNamesSet[rn] = struct{}{} } - maxGroups := getInt64WithDefault(opts.Query, "group_limit", -1) - nextToken := opts.Query.Get("group_next_token") - if nextToken != "" { - if _, err := base64.URLEncoding.DecodeString(nextToken); err != nil { - nextToken = "" - } - } - groupedRules := getGroupedRules(log, ruleList, ruleNamesSet, opts.AllowedNamespaces) rulesTotals := make(map[string]int64, len(groupedRules)) - var newToken string - foundToken := false for _, rg := range groupedRules { - if nextToken != "" && !foundToken { - if !tokenGreaterThanOrEqual(getRuleGroupNextToken(rg.Folder, rg.GroupKey.RuleGroup), nextToken) { - continue - } - foundToken = true - } - - if maxGroups > -1 && len(ruleResponse.Data.RuleGroups) == int(maxGroups) { - newToken = getRuleGroupNextToken(rg.Folder, rg.GroupKey.RuleGroup) - break - } - ruleGroup, totals := toRuleGroup(log, rg.GroupKey, rg.Folder, rg.Rules, provenanceRecords, limitAlertsPerRule, stateFilterSet, matchers, labelOptions, ruleStatusMutator, alertStateMutator) ruleGroup.Totals = totals for k, v := range totals { @@ -546,7 +532,7 @@ func PrepareRuleGroupStatuses(log log.Logger, store ListAlertRulesStore, opts Ru } } - ruleResponse.Data.NextToken = newToken + ruleResponse.Data.NextToken = continueToken // Only return Totals if there is no pagination if maxGroups == -1 { @@ -556,18 +542,6 @@ func PrepareRuleGroupStatuses(log log.Logger, store ListAlertRulesStore, opts Ru return ruleResponse } -func getRuleGroupNextToken(namespace, group string) string { - return base64.URLEncoding.EncodeToString([]byte(namespace + "/" + group)) -} - -// Returns true if tokenA >= tokenB -func tokenGreaterThanOrEqual(tokenA string, tokenB string) bool { - decodedTokenA, _ := base64.URLEncoding.DecodeString(tokenA) - decodedTokenB, _ := base64.URLEncoding.DecodeString(tokenB) - - return string(decodedTokenA) >= string(decodedTokenB) -} - type ruleGroup struct { Folder string GroupKey ngmodels.AlertRuleGroupKey diff --git a/pkg/services/ngalert/models/alert_rule.go b/pkg/services/ngalert/models/alert_rule.go index b4a4a94eaba..b1f415f929d 100644 --- a/pkg/services/ngalert/models/alert_rule.go +++ b/pkg/services/ngalert/models/alert_rule.go @@ -2,6 +2,7 @@ package models import ( "context" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -848,6 +849,61 @@ type GetAlertRulesGroupByRuleUIDQuery struct { OrgID int64 } +type RuleTypeFilter int + +const ( + RuleTypeFilterAll RuleTypeFilter = iota + RuleTypeFilterAlerting + RuleTypeFilterRecording +) + +type ListAlertRulesByGroupQuery struct { + OrgID int64 + RuleUIDs []string + NamespaceUIDs []string + ExcludeOrgs []int64 + RuleGroups []string + + // DashboardUID and PanelID are optional and allow filtering rules + // to return just those for a dashboard and panel. + DashboardUID string + PanelID int64 + + ReceiverName string + TimeIntervalName string + + HasPrometheusRuleDefinition *bool + + RuleType RuleTypeFilter + + GroupLimit int64 // Number of groups to fetch + GroupContinueToken string // Token for per-group pagination +} + +type GroupCursor struct { + NamespaceUID string `json:"n"` + RuleGroup string `json:"g"` +} + +func EncodeGroupCursor(c GroupCursor) string { + data, _ := json.Marshal(c) + return base64.URLEncoding.EncodeToString(data) +} + +func DecodeGroupCursor(token string) (GroupCursor, error) { + var c GroupCursor + data, err := base64.URLEncoding.DecodeString(token) + if err != nil { + return c, fmt.Errorf("failed to decode group token: %w", err) + } + + if err := json.Unmarshal(data, &c); err != nil { + return c, fmt.Errorf("failed to unmarshal group cursor: %w", err) + } + + return c, nil +} + // ListAlertRulesQuery is the query for listing alert rules type ListAlertRulesQuery struct { OrgID int64 diff --git a/pkg/services/ngalert/prom/convert.go b/pkg/services/ngalert/prom/convert.go index 97eece4edfc..4b8137f347b 100644 --- a/pkg/services/ngalert/prom/convert.go +++ b/pkg/services/ngalert/prom/convert.go @@ -59,6 +59,9 @@ type Config struct { RecordingRules RulesConfig AlertRules RulesConfig NotificationSettings []models.NotificationSettings + // ExtraLabels are labels that will be added to all rules during conversion. + // These labels have the lowest precedence and can be overridden by group or rule labels. + ExtraLabels map[string]string } // RulesConfig contains configuration that applies to either recording or alerting rules. @@ -232,7 +235,8 @@ func (p *Converter) convertRule(orgID int64, namespaceUID string, promGroup Prom title = rule.Alert } - labels := make(map[string]string, len(rule.Labels)+len(promGroup.Labels)+1) + labels := make(map[string]string, len(rule.Labels)+len(promGroup.Labels)+len(p.cfg.ExtraLabels)+1) + maps.Copy(labels, p.cfg.ExtraLabels) maps.Copy(labels, promGroup.Labels) maps.Copy(labels, rule.Labels) diff --git a/pkg/services/ngalert/prom/convert_test.go b/pkg/services/ngalert/prom/convert_test.go index befa524f468..5d7b9470d00 100644 --- a/pkg/services/ngalert/prom/convert_test.go +++ b/pkg/services/ngalert/prom/convert_test.go @@ -659,6 +659,73 @@ func TestPrometheusRulesToGrafana_GroupLabels(t *testing.T) { }) } +func TestPrometheusRulesToGrafana_ExtraLabels(t *testing.T) { + cfg := Config{ + DatasourceUID: "datasource-uid", + DatasourceType: datasources.DS_PROMETHEUS, + DefaultInterval: 2 * time.Minute, + ExtraLabels: map[string]string{ + "extra_label": "extra_value", + "common_label": "extra_value", + "rule_label": "value_from_extra_labels", + }, + } + converter, err := NewConverter(cfg) + require.NoError(t, err) + + t.Run("extra labels are merged with group and rule labels", func(t *testing.T) { + promGroup := PrometheusRuleGroup{ + Name: "test-group-1", + Interval: prommodel.Duration(10 * time.Second), + Labels: map[string]string{ + "group_label": "group_value", + "common_label": "group_value", + }, + Rules: []PrometheusRule{ + { + Alert: "alert-1", + Expr: "cpu_usage > 80", + Labels: map[string]string{ + "rule_label": "rule_value", + }, + }, + }, + } + + grafanaGroup, err := converter.PrometheusRulesToGrafana(1, "namespace", promGroup) + require.NoError(t, err) + require.Len(t, grafanaGroup.Rules, 1) + + expectedLabels := withInternalLabel(map[string]string{ + "extra_label": "extra_value", + "common_label": "group_value", + "group_label": "group_value", + "rule_label": "rule_value", + }) + require.Equal(t, expectedLabels, grafanaGroup.Rules[0].Labels) + }) + + t.Run("extra labels are applied to recording rules", func(t *testing.T) { + promGroup := PrometheusRuleGroup{ + Name: "test-group-2", + Interval: prommodel.Duration(10 * time.Second), + Rules: []PrometheusRule{ + { + Record: "http_requests_total:rate5m", + Expr: "rate(http_requests_total[5m])", + }, + }, + } + + grafanaGroup, err := converter.PrometheusRulesToGrafana(1, "namespace", promGroup) + require.NoError(t, err) + require.Len(t, grafanaGroup.Rules, 1) + + expectedLabels := withInternalLabel(cfg.ExtraLabels) + require.Equal(t, expectedLabels, grafanaGroup.Rules[0].Labels) + }) +} + func TestPrometheusRulesToGrafana_UID(t *testing.T) { orgID := int64(1) namespace := "some-namespace" diff --git a/pkg/services/ngalert/remote/alertmanager.go b/pkg/services/ngalert/remote/alertmanager.go index 8329c7bb8de..7c1e1dd4060 100644 --- a/pkg/services/ngalert/remote/alertmanager.go +++ b/pkg/services/ngalert/remote/alertmanager.go @@ -2,10 +2,10 @@ package remote import ( "context" + "crypto/md5" "encoding/base64" "encoding/json" "fmt" - "hash/fnv" "net/http" "net/url" "strings" @@ -16,12 +16,12 @@ import ( "github.com/grafana/alerting/definition" alertingModels "github.com/grafana/alerting/models" alertingNotify "github.com/grafana/alerting/notify" - "github.com/grafana/alerting/utils/hash" amalert "github.com/prometheus/alertmanager/api/v2/client/alert" amalertgroup "github.com/prometheus/alertmanager/api/v2/client/alertgroup" amgeneral "github.com/prometheus/alertmanager/api/v2/client/general" amsilence "github.com/prometheus/alertmanager/api/v2/client/silence" "github.com/prometheus/client_golang/prometheus" + "gopkg.in/yaml.v3" "github.com/grafana/grafana/pkg/infra/log" @@ -73,9 +73,6 @@ type Alertmanager struct { amClient *remoteClient.Alertmanager mimirClient remoteClient.MimirClient - - promoteConfig bool - externalURL string } type AlertmanagerConfig struct { @@ -130,10 +127,13 @@ func NewAlertmanager(ctx context.Context, cfg AlertmanagerConfig, store stateSto logger := log.New("ngalert.remote.alertmanager") mcCfg := &remoteClient.Config{ - Logger: logger, - Password: cfg.BasicAuthPassword, - TenantID: cfg.TenantID, - URL: u, + Logger: logger, + Password: cfg.BasicAuthPassword, + TenantID: cfg.TenantID, + URL: u, + PromoteConfig: cfg.PromoteConfig, + ExternalURL: cfg.ExternalURL, + Smtp: cfg.SmtpConfig, } mc, err := remoteClient.New(mcCfg, metrics, tracer) if err != nil { @@ -188,10 +188,7 @@ func NewAlertmanager(ctx context.Context, cfg AlertmanagerConfig, store stateSto syncInterval: cfg.SyncInterval, tenantID: cfg.TenantID, url: cfg.URL, - - externalURL: cfg.ExternalURL, - promoteConfig: cfg.PromoteConfig, - smtp: cfg.SmtpConfig, + smtp: cfg.SmtpConfig, } // Parse the default configuration once and remember its hash so we can compare it later. @@ -199,11 +196,15 @@ func NewAlertmanager(ctx context.Context, cfg AlertmanagerConfig, store stateSto // (grouping, group timing, time intervals etc) changes the autogenerated configuration. // The `default` flag is sent to the remote Alertmanager for informational purposes, so we can tolerate this. err = func() error { - defaultCfg, err := am.buildConfiguration(ctx, []byte(cfg.DefaultConfig), 0) + defaultCfg, err := am.buildConfiguration(ctx, []byte(cfg.DefaultConfig)) if err != nil { return fmt.Errorf("unable to build default configuration: %w", err) } - am.defaultConfigHash = defaultCfg.Hash + rawDefaultCfg, err := json.Marshal(defaultCfg) + if err != nil { + return fmt.Errorf("unable to marshal default configuration: %w", err) + } + am.defaultConfigHash = fmt.Sprintf("%x", md5.Sum(rawDefaultCfg)) return nil }() if err != nil { @@ -264,16 +265,22 @@ func (am *Alertmanager) checkReadiness(ctx context.Context) error { // CompareAndSendConfiguration checks whether a given configuration is being used by the remote Alertmanager. // If not, it sends the configuration to the remote Alertmanager. func (am *Alertmanager) CompareAndSendConfiguration(ctx context.Context, config *models.AlertConfiguration) error { - payload, err := am.buildConfiguration(ctx, []byte(config.AlertmanagerConfiguration), config.CreatedAt) + payload, err := am.buildConfiguration(ctx, []byte(config.AlertmanagerConfiguration)) if err != nil { return fmt.Errorf("unable to build configuration: %w", err) } + rawPayload, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("unable to marshal decrypted configuration: %w", err) + } + configHash := fmt.Sprintf("%x", md5.Sum(rawPayload)) + // Send the configuration only if we need to. - if !am.shouldSendConfig(ctx, payload.Hash) { + if !am.shouldSendConfig(ctx, configHash) { return nil } - return am.sendConfiguration(ctx, payload) + return am.sendConfiguration(ctx, payload, configHash, config.CreatedAt, am.isDefaultConfiguration(configHash)) } func (am *Alertmanager) isDefaultConfiguration(configHash string) bool { @@ -296,31 +303,31 @@ func decrypter(ctx context.Context, crypto Crypto) models.DecryptFn { // buildConfiguration takes a raw Alertmanager configuration and returns a config that the remote Alertmanager can use. // It parses the initial configuration, adds auto-generated routes, decrypts receivers, and merges the extra configs. -func (am *Alertmanager) buildConfiguration(ctx context.Context, raw []byte, createdAtEpoch int64) (remoteClient.UserGrafanaConfig, error) { +func (am *Alertmanager) buildConfiguration(ctx context.Context, raw []byte) (remoteClient.GrafanaAlertmanagerConfig, error) { c, err := notifier.Load(raw) if err != nil { - return remoteClient.UserGrafanaConfig{}, err + return remoteClient.GrafanaAlertmanagerConfig{}, err } // Add auto-generated routes and decrypt before comparing. if err := am.autogenFn(ctx, am.log, am.orgID, &c.AlertmanagerConfig, true); err != nil { - return remoteClient.UserGrafanaConfig{}, err + return remoteClient.GrafanaAlertmanagerConfig{}, err } // Decrypt the receivers in the configuration. decryptedReceivers, err := legacy_storage.DecryptedReceivers(c.AlertmanagerConfig.Receivers, decrypter(ctx, am.crypto)) if err != nil { - return remoteClient.UserGrafanaConfig{}, fmt.Errorf("unable to decrypt receivers: %w", err) + return remoteClient.GrafanaAlertmanagerConfig{}, fmt.Errorf("unable to decrypt receivers: %w", err) } c.AlertmanagerConfig.Receivers = decryptedReceivers if err := am.crypto.DecryptExtraConfigs(ctx, c); err != nil { - return remoteClient.UserGrafanaConfig{}, fmt.Errorf("unable to decrypt extra configs: %w", err) + return remoteClient.GrafanaAlertmanagerConfig{}, fmt.Errorf("unable to decrypt extra configs: %w", err) } mergeResult, err := c.GetMergedAlertmanagerConfig() if err != nil { - return remoteClient.UserGrafanaConfig{}, fmt.Errorf("unable to get merged Alertmanager configuration: %w", err) + return remoteClient.GrafanaAlertmanagerConfig{}, fmt.Errorf("unable to get merged Alertmanager configuration: %w", err) } var templates []definition.PostableApiTemplate @@ -328,31 +335,22 @@ func (am *Alertmanager) buildConfiguration(ctx context.Context, raw []byte, crea templates = definition.TemplatesMapToPostableAPITemplates(c.ExtraConfigs[0].TemplateFiles, definition.MimirTemplateKind) } - payload := remoteClient.UserGrafanaConfig{ - GrafanaAlertmanagerConfig: remoteClient.GrafanaAlertmanagerConfig{ - TemplateFiles: c.TemplateFiles, - AlertmanagerConfig: mergeResult.Config, - Templates: templates, - }, - CreatedAt: createdAtEpoch, - Promoted: am.promoteConfig, - ExternalURL: am.externalURL, - SmtpConfig: am.smtp, - } - - cfgHash, err := calculateUserGrafanaConfigHash(payload) - if err != nil { - am.log.Error("Unable to calculate hash of the configuration. Using the empty string", "error", err) - cfgHash = "" - } - payload.Hash = cfgHash - payload.Default = am.isDefaultConfiguration(cfgHash) - return payload, nil + return remoteClient.GrafanaAlertmanagerConfig{ + TemplateFiles: c.TemplateFiles, + AlertmanagerConfig: mergeResult.Config, + Templates: templates, + }, nil } -func (am *Alertmanager) sendConfiguration(ctx context.Context, cfg remoteClient.UserGrafanaConfig) error { +func (am *Alertmanager) sendConfiguration(ctx context.Context, cfg remoteClient.GrafanaAlertmanagerConfig, hash string, createdAt int64, isDefault bool) error { am.metrics.ConfigSyncsTotal.Inc() - if err := am.mimirClient.CreateGrafanaAlertmanagerConfig(ctx, &cfg); err != nil { + if err := am.mimirClient.CreateGrafanaAlertmanagerConfig( + ctx, + cfg, + hash, + createdAt, + isDefault, + ); err != nil { am.metrics.ConfigSyncErrorsTotal.Inc() return err } @@ -424,25 +422,40 @@ func (am *Alertmanager) SaveAndApplyConfig(ctx context.Context, cfg *apimodels.P return err } - payload, err := am.buildConfiguration(ctx, rawCopy, time.Now().Unix()) + payload, err := am.buildConfiguration(ctx, rawCopy) if err != nil { return fmt.Errorf("unable to build configuration: %w", err) } - return am.sendConfiguration(ctx, payload) + rawCfg, err := json.Marshal(payload) + if err != nil { + return err + } + hash := fmt.Sprintf("%x", md5.Sum(rawCfg)) + + return am.sendConfiguration(ctx, payload, hash, time.Now().Unix(), false) } // SaveAndApplyDefaultConfig sends the default Grafana Alertmanager configuration to the remote Alertmanager. func (am *Alertmanager) SaveAndApplyDefaultConfig(ctx context.Context) error { am.log.Debug("Sending default configuration to a remote Alertmanager", "url", am.url) - payload, err := am.buildConfiguration(ctx, []byte(am.defaultConfig), time.Now().Unix()) + payload, err := am.buildConfiguration(ctx, []byte(am.defaultConfig)) if err != nil { return fmt.Errorf("unable to build default configuration: %w", err) } - payload.Default = true // override default status + + rawCfg, err := json.Marshal(payload) + if err != nil { + return err + } + hash := fmt.Sprintf("%x", md5.Sum(rawCfg)) + return am.sendConfiguration( ctx, payload, + hash, + time.Now().Unix(), + true, ) } @@ -683,29 +696,37 @@ func (am *Alertmanager) getFullState(ctx context.Context) (string, error) { // shouldSendConfig compares the remote Alertmanager configuration with our local one. // It returns true if the configurations are different. func (am *Alertmanager) shouldSendConfig(ctx context.Context, hash string) bool { - if hash == "" { // empty hash means that something went wrong while calculating it. In this case, always send the config. - return true - } rc, err := am.mimirClient.GetGrafanaAlertmanagerConfig(ctx) if err != nil { // Log the error and return true so we try to upload our config anyway. am.log.Warn("Unable to get the remote Alertmanager configuration for comparison, sending the configuration without comparing", "err", err) return true } - if rc.Hash != hash { - am.log.Debug("Hash of the remote Alertmanager configuration is different, sending the configuration", "remote", rc.Hash, "local", hash) + + if rc.Promoted != am.mimirClient.ShouldPromoteConfig() { return true } - return false -} -func calculateUserGrafanaConfigHash(config remoteClient.UserGrafanaConfig) (string, error) { - // Ignore some fields when calculating the hash. Make sure the original struct is not modified after that. - config.Default = false - config.CreatedAt = 0 // ignore createdAt to support comparison with hash of default config - config.Hash = "" + // Compare SMTP configs. + if rc.SmtpConfig.EhloIdentity != am.smtp.EhloIdentity || + rc.SmtpConfig.Password != am.smtp.Password || + rc.SmtpConfig.FromAddress != am.smtp.FromAddress || + rc.SmtpConfig.FromName != am.smtp.FromName || + rc.SmtpConfig.Host != am.smtp.Host || + rc.SmtpConfig.SkipVerify != am.smtp.SkipVerify || + rc.SmtpConfig.StartTLSPolicy != am.smtp.StartTLSPolicy || + len(rc.SmtpConfig.StaticHeaders) != len(am.smtp.StaticHeaders) || + rc.SmtpConfig.User != am.smtp.User { + am.log.Debug("SMTP config is different, sending the configuration to the remote Alertmanager") + return true + } - hasher := fnv.New64a() - hash.DeepHashObject(hasher, &config) - return fmt.Sprintf("%x", hasher.Sum64()), nil + for k, v := range rc.SmtpConfig.StaticHeaders { + if value, ok := am.smtp.StaticHeaders[k]; !ok || v != value { + am.log.Debug("SMTP static headers are different, sending the configuration to the remote Alertmanager") + return true + } + } + + return rc.Hash != hash } diff --git a/pkg/services/ngalert/remote/alertmanager_test.go b/pkg/services/ngalert/remote/alertmanager_test.go index 10ea5e45eaf..6ce6abb8994 100644 --- a/pkg/services/ngalert/remote/alertmanager_test.go +++ b/pkg/services/ngalert/remote/alertmanager_test.go @@ -19,13 +19,10 @@ import ( "time" "github.com/go-openapi/strfmt" - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" amv2 "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/client_golang/prometheus" - common_config "github.com/prometheus/common/config" "github.com/stretchr/testify/require" alertingClusterPB "github.com/grafana/alerting/cluster/clusterpb" @@ -504,6 +501,15 @@ func TestCompareAndSendConfiguration(t *testing.T) { AlertmanagerConfig: testAutogenRoutes.AlertmanagerConfig, } + // Calculate hashes for expected configurations + cfgWithDecryptedSecretBytes, err := json.Marshal(cfgWithDecryptedSecret) + require.NoError(t, err) + cfgWithDecryptedSecretHash := fmt.Sprintf("%x", md5.Sum(cfgWithDecryptedSecretBytes)) + + cfgWithAutogenRoutesBytes, err := json.Marshal(cfgWithAutogenRoutes) + require.NoError(t, err) + cfgWithAutogenRoutesHash := fmt.Sprintf("%x", md5.Sum(cfgWithAutogenRoutesBytes)) + cfgWithExtraUnmergedBytes, err := testData.ReadFile(path.Join("test-data", "config-with-extra.json")) require.NoError(t, err) cfgWithExtraUnmerged, err := notifier.Load(cfgWithExtraUnmergedBytes) @@ -515,6 +521,9 @@ func TestCompareAndSendConfiguration(t *testing.T) { AlertmanagerConfig: r.Config, Templates: definition.TemplatesMapToPostableAPITemplates(cfgWithExtraUnmerged.ExtraConfigs[0].TemplateFiles, definition.MimirTemplateKind), } + cfgWithExtraMergedBytes, err := json.Marshal(cfgWithExtraMerged) + require.NoError(t, err) + cfgWithExtraMergedHash := fmt.Sprintf("%x", md5.Sum(cfgWithExtraMergedBytes)) tests := []struct { name string @@ -557,6 +566,7 @@ func TestCompareAndSendConfiguration(t *testing.T) { NoopAutogenFn, &client.UserGrafanaConfig{ GrafanaAlertmanagerConfig: cfgWithDecryptedSecret, + Hash: cfgWithDecryptedSecretHash, }, nil, }, @@ -566,6 +576,7 @@ func TestCompareAndSendConfiguration(t *testing.T) { testAutogenFn, &client.UserGrafanaConfig{ GrafanaAlertmanagerConfig: cfgWithAutogenRoutes, + Hash: cfgWithAutogenRoutesHash, }, nil, }, @@ -575,6 +586,7 @@ func TestCompareAndSendConfiguration(t *testing.T) { autogenFn: NoopAutogenFn, expCfg: &client.UserGrafanaConfig{ GrafanaAlertmanagerConfig: cfgWithExtraMerged, + Hash: cfgWithExtraMergedHash, }, }, } @@ -602,26 +614,9 @@ func TestCompareAndSendConfiguration(t *testing.T) { err = am.CompareAndSendConfiguration(ctx, &cfg) if len(test.expErrContains) == 0 { require.NoError(tt, err) - - var gotCfg client.UserGrafanaConfig - require.NoError(tt, json.Unmarshal([]byte(got), &gotCfg)) - - require.NotEmpty(tt, gotCfg.Hash) - require.Empty(tt, cmp.Diff(test.expCfg, &gotCfg, - cmpopts.IgnoreFields(client.UserGrafanaConfig{}, "Hash"), // do not compare hashes because the config is processed slightly different: empty maps are nils. - cmpopts.EquateEmpty(), - cmpopts.IgnoreUnexported( - time.Location{}, - labels.Matcher{}, - common_config.ProxyConfig{}))) - - got1 := got - got = "" - err = am.CompareAndSendConfiguration(ctx, &cfg) + rawCfg, err := json.Marshal(test.expCfg) require.NoError(tt, err) - - got2 := got - require.Equalf(tt, got1, got2, "Configuration is not idempotent") + require.JSONEq(tt, string(rawCfg), got) return } for _, expErr := range test.expErrContains { @@ -820,7 +815,12 @@ receivers: require.NotNil(t, extraReceiver) require.Len(t, extraReceiver.EmailConfigs, 1) require.Equal(t, "alerts@grafana.com", extraReceiver.EmailConfigs[0].To) - require.NotEmpty(t, configSent.Hash) + + // Verify the config hash + expectedConfigBytes, err := json.Marshal(configSent.GrafanaAlertmanagerConfig) + require.NoError(t, err) + expectedHash := fmt.Sprintf("%x", md5.Sum(expectedConfigBytes)) + require.Equal(t, expectedHash, configSent.Hash) } func TestCompareAndSendConfigurationWithExtraConfigs(t *testing.T) { @@ -934,7 +934,10 @@ receivers: require.True(t, found) // Verify the config hash - require.NotEmpty(t, configSent.Hash) + expectedConfigBytes, err := json.Marshal(configSent.GrafanaAlertmanagerConfig) + require.NoError(t, err) + expectedHash := fmt.Sprintf("%x", md5.Sum(expectedConfigBytes)) + require.Equal(t, expectedHash, configSent.Hash) } func TestIntegrationRemoteAlertmanagerConfiguration(t *testing.T) { @@ -958,10 +961,11 @@ func TestIntegrationRemoteAlertmanagerConfiguration(t *testing.T) { DefaultConfig: defaultGrafanaConfig, } + testConfigHash := fmt.Sprintf("%x", md5.Sum([]byte(testGrafanaConfig))) testConfigCreatedAt := time.Now().Unix() testConfig := &ngmodels.AlertConfiguration{ AlertmanagerConfiguration: testGrafanaConfig, - ConfigurationHash: "", + ConfigurationHash: testConfigHash, ConfigurationVersion: "v2", CreatedAt: testConfigCreatedAt, OrgID: 1, @@ -1008,6 +1012,7 @@ func TestIntegrationRemoteAlertmanagerConfiguration(t *testing.T) { rawCfg, err := json.Marshal(config.GrafanaAlertmanagerConfig) require.NoError(t, err) require.JSONEq(t, testGrafanaConfig, string(rawCfg)) + require.Equal(t, testConfigHash, config.Hash) require.Equal(t, testConfigCreatedAt, config.CreatedAt) require.Equal(t, testConfig.Default, config.Default) @@ -1033,6 +1038,7 @@ func TestIntegrationRemoteAlertmanagerConfiguration(t *testing.T) { rawCfg, err := json.Marshal(config.GrafanaAlertmanagerConfig) require.NoError(t, err) require.JSONEq(t, testGrafanaConfig, string(rawCfg)) + require.Equal(t, testConfigHash, config.Hash) require.Equal(t, testConfigCreatedAt, config.CreatedAt) require.False(t, config.Default) @@ -1079,6 +1085,9 @@ func TestIntegrationRemoteAlertmanagerConfiguration(t *testing.T) { require.JSONEq(t, testGrafanaConfigWithSecret, string(got)) + // Verify that the hash is calculated from the final configuration, including simplified routing + expectedHash := fmt.Sprintf("%x", md5.Sum(got)) + require.Equal(t, expectedHash, config.Hash, "Hash should be calculated from the final processed configuration") require.False(t, config.Default) // An error while adding auto-generated rutes should be returned. @@ -1105,6 +1114,7 @@ func TestIntegrationRemoteAlertmanagerConfiguration(t *testing.T) { require.NoError(t, err) require.JSONEq(t, string(want), string(got)) + require.Equal(t, fmt.Sprintf("%x", md5.Sum(want)), config.Hash) require.True(t, config.Default) // An error while adding auto-generated rutes should be returned. diff --git a/pkg/services/ngalert/remote/client/alertmanager_configuration.go b/pkg/services/ngalert/remote/client/alertmanager_configuration.go index a53132a8812..b812c146687 100644 --- a/pkg/services/ngalert/remote/client/alertmanager_configuration.go +++ b/pkg/services/ngalert/remote/client/alertmanager_configuration.go @@ -39,6 +39,10 @@ type UserGrafanaConfig struct { SmtpConfig SmtpConfig `json:"smtp_config"` } +func (mc *Mimir) ShouldPromoteConfig() bool { + return mc.promoteConfig +} + func (mc *Mimir) GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafanaConfig, error) { gc := &UserGrafanaConfig{} response := successResponse{ @@ -58,8 +62,16 @@ func (mc *Mimir) GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafana return gc, nil } -func (mc *Mimir) CreateGrafanaAlertmanagerConfig(ctx context.Context, cfg *UserGrafanaConfig) error { - payload, err := definition.MarshalJSONWithSecrets(cfg) +func (mc *Mimir) CreateGrafanaAlertmanagerConfig(ctx context.Context, cfg GrafanaAlertmanagerConfig, hash string, createdAt int64, isDefault bool) error { + payload, err := definition.MarshalJSONWithSecrets(&UserGrafanaConfig{ + GrafanaAlertmanagerConfig: cfg, + Hash: hash, + CreatedAt: createdAt, + Default: isDefault, + Promoted: mc.promoteConfig, + ExternalURL: mc.externalURL, + SmtpConfig: mc.smtpConfig, + }) if err != nil { return err } diff --git a/pkg/services/ngalert/remote/client/mimir.go b/pkg/services/ngalert/remote/client/mimir.go index d8f9a51f327..1533e24c208 100644 --- a/pkg/services/ngalert/remote/client/mimir.go +++ b/pkg/services/ngalert/remote/client/mimir.go @@ -30,21 +30,26 @@ type MimirClient interface { DeleteGrafanaAlertmanagerState(ctx context.Context) error GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafanaConfig, error) - CreateGrafanaAlertmanagerConfig(ctx context.Context, config *UserGrafanaConfig) error + CreateGrafanaAlertmanagerConfig(ctx context.Context, configuration GrafanaAlertmanagerConfig, hash string, createdAt int64, isDefault bool) error DeleteGrafanaAlertmanagerConfig(ctx context.Context) error TestTemplate(ctx context.Context, c alertingNotify.TestTemplatesConfigBodyParams) (*alertingNotify.TestTemplatesResults, error) TestReceivers(ctx context.Context, c alertingNotify.TestReceiversConfigBodyParams) (*alertingNotify.TestReceiversResult, int, error) + ShouldPromoteConfig() bool + // Mimir implements an extended version of the receivers API under a different path. GetReceivers(ctx context.Context) ([]apimodels.Receiver, error) } type Mimir struct { - client client.Requester - endpoint *url.URL - logger log.Logger - metrics *metrics.RemoteAlertmanager + client client.Requester + endpoint *url.URL + logger log.Logger + metrics *metrics.RemoteAlertmanager + promoteConfig bool + externalURL string + smtpConfig SmtpConfig } type SmtpConfig struct { @@ -64,7 +69,10 @@ type Config struct { TenantID string Password string - Logger log.Logger + Logger log.Logger + PromoteConfig bool + ExternalURL string + Smtp SmtpConfig } // successResponse represents a successful response from the Mimir API. @@ -102,10 +110,13 @@ func New(cfg *Config, metrics *metrics.RemoteAlertmanager, tracer tracing.Tracer trc := client.NewTracedClient(tc, tracer, "remote.alertmanager.client") return &Mimir{ - endpoint: cfg.URL, - client: trc, - logger: cfg.Logger, - metrics: metrics, + endpoint: cfg.URL, + client: trc, + logger: cfg.Logger, + metrics: metrics, + promoteConfig: cfg.PromoteConfig, + externalURL: cfg.ExternalURL, + smtpConfig: cfg.Smtp, }, nil } diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index c535bbe4379..ab8676c754d 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -582,6 +582,186 @@ func (st DBstore) CountInFolders(ctx context.Context, orgID int64, folderUIDs [] return count, err } +func (st DBstore) ListAlertRulesByGroup(ctx context.Context, query *ngmodels.ListAlertRulesByGroupQuery) (result ngmodels.RulesGroup, nextToken string, err error) { + err = st.SQLStore.WithDbSession(ctx, func(sess *db.Session) error { + q := sess.Table("alert_rule") + + if query.OrgID >= 0 { + q = q.Where("org_id = ?", query.OrgID) + } + + if query.DashboardUID != "" { + q = q.Where("dashboard_uid = ?", query.DashboardUID) + if query.PanelID != 0 { + q = q.Where("panel_id = ?", query.PanelID) + } + } + + if len(query.NamespaceUIDs) > 0 { + args, in := getINSubQueryArgs(query.NamespaceUIDs) + q = q.Where(fmt.Sprintf("namespace_uid IN (%s)", strings.Join(in, ",")), args...) + } + + if len(query.RuleUIDs) > 0 { + args, in := getINSubQueryArgs(query.RuleUIDs) + q = q.Where(fmt.Sprintf("uid IN (%s)", strings.Join(in, ",")), args...) + } + + var groupsMap map[string]struct{} + if len(query.RuleGroups) > 0 { + groupsMap = make(map[string]struct{}) + args, in := getINSubQueryArgs(query.RuleGroups) + q = q.Where(fmt.Sprintf("rule_group IN (%s)", strings.Join(in, ",")), args...) + for _, group := range query.RuleGroups { + groupsMap[group] = struct{}{} + } + } + + if query.ReceiverName != "" { + q, err = st.filterByContentInNotificationSettings(query.ReceiverName, q) + if err != nil { + return err + } + } + + if query.TimeIntervalName != "" { + q, err = st.filterByContentInNotificationSettings(query.TimeIntervalName, q) + if err != nil { + return err + } + } + + if query.HasPrometheusRuleDefinition != nil { + q, err = st.filterWithPrometheusRuleDefinition(*query.HasPrometheusRuleDefinition, q) + if err != nil { + return err + } + } + + switch query.RuleType { + case ngmodels.RuleTypeFilterAlerting: + q = q.Where("record = ''") + case ngmodels.RuleTypeFilterRecording: + q = q.Where("record != ''") + case ngmodels.RuleTypeFilterAll: + // no additional filter + default: + return fmt.Errorf("unknown rule type filter %q", query.RuleType) + } + + // Order by group first, then by rule index within group + q = q.Asc("namespace_uid", "rule_group", "rule_group_idx", "id") + + var cursor ngmodels.GroupCursor + if query.GroupContinueToken != "" { + // only set the cursor if it's valid, otherwise we'll start from the beginning + if cur, err := ngmodels.DecodeGroupCursor(query.GroupContinueToken); err == nil { + cursor = cur + } + } + + // Build group cursor condition + if cursor.NamespaceUID != "" { + q = buildGroupCursorCondition(q, cursor) + } + + // No arbitrary fetch limit - let the loop control pagination + alertRules := make([]*ngmodels.AlertRule, 0) + rule := new(alertRule) + rows, err := q.Rows(rule) + if err != nil { + return err + } + defer func() { + _ = rows.Close() + }() + + // Process rules and implement per-group pagination + var groupsFetched int64 + for rows.Next() { + rule := new(alertRule) + err = rows.Scan(rule) + if err != nil { + st.Logger.Error("Invalid rule found in DB store, ignoring it", "func", "ListAlertRulesByGroup", "error", err) + continue + } + + converted, err := alertRuleToModelsAlertRule(*rule, st.Logger) + if err != nil { + st.Logger.Error("Invalid rule found in DB store, cannot convert, ignoring it", "func", "ListAlertRulesByGroup", "error", err) + continue + } + + // Check if we've moved to a new group + key := ngmodels.GroupCursor{ + NamespaceUID: converted.NamespaceUID, + RuleGroup: converted.RuleGroup, + } + if key != cursor { + // Check if we've reached the group limit + if query.GroupLimit > 0 && groupsFetched == query.GroupLimit { + // Generate next token for the next group + nextToken = ngmodels.EncodeGroupCursor(cursor) + break + } + + // Reset for new group + cursor = key + groupsFetched++ + } + + // Apply post-query filters + if !shouldIncludeRule(&converted, query, groupsMap) { + continue + } + + alertRules = append(alertRules, &converted) + } + + result = alertRules + return nil + }) + return result, nextToken, err +} + +func buildGroupCursorCondition(sess *xorm.Session, c ngmodels.GroupCursor) *xorm.Session { + return sess.Where("(namespace_uid > ?)", c.NamespaceUID). + Or("(namespace_uid = ? AND rule_group > ?)", c.NamespaceUID, c.RuleGroup) +} + +func shouldIncludeRule(rule *ngmodels.AlertRule, query *ngmodels.ListAlertRulesByGroupQuery, groupsMap map[string]struct{}) bool { + if query.ReceiverName != "" { + if !slices.ContainsFunc(rule.NotificationSettings, func(settings ngmodels.NotificationSettings) bool { + return settings.Receiver == query.ReceiverName + }) { + return false + } + } + + if query.TimeIntervalName != "" { + if !slices.ContainsFunc(rule.NotificationSettings, func(settings ngmodels.NotificationSettings) bool { + return slices.Contains(settings.MuteTimeIntervals, query.TimeIntervalName) || + slices.Contains(settings.ActiveTimeIntervals, query.TimeIntervalName) + }) { + return false + } + } + + if query.HasPrometheusRuleDefinition != nil { + if *query.HasPrometheusRuleDefinition != rule.HasPrometheusRuleDefinition() { + return false + } + } + + if groupsMap != nil { + if _, ok := groupsMap[rule.RuleGroup]; !ok { + return false + } + } + + return true +} + // ListAlertRules is a handler for retrieving alert rules of specific organisation. func (st DBstore) ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (result ngmodels.RulesGroup, err error) { err = st.SQLStore.WithDbSession(ctx, func(sess *db.Session) error { diff --git a/pkg/services/ngalert/store/alert_rule_test.go b/pkg/services/ngalert/store/alert_rule_test.go index 74f51fd305a..a512b5949b4 100644 --- a/pkg/services/ngalert/store/alert_rule_test.go +++ b/pkg/services/ngalert/store/alert_rule_test.go @@ -1596,14 +1596,14 @@ func TestIntegrationGetRuleVersions(t *testing.T) { // createAlertRule creates an alert rule in the database and returns it. // If a generator is not specified, uniqueness of primary key is not guaranteed. -func createRule(t *testing.T, store *DBstore, generator *models.AlertRuleGenerator) *models.AlertRule { - t.Helper() +func createRule(tb testing.TB, store *DBstore, generator *models.AlertRuleGenerator) *models.AlertRule { + tb.Helper() if generator == nil { generator = models.RuleGen.With(models.RuleMuts.WithIntervalMatching(store.Cfg.BaseInterval)) } rule := generator.GenerateRef() converted, err := alertRuleFromModelsAlertRule(*rule) - require.NoError(t, err) + require.NoError(tb, err) err = store.SQLStore.WithDbSession(context.Background(), func(sess *db.Session) error { converted.ID = 0 _, err := sess.Table(alertRule{}).InsertOne(&converted) @@ -1622,12 +1622,12 @@ func createRule(t *testing.T, store *DBstore, generator *models.AlertRuleGenerat rule = &r return err }) - require.NoError(t, err) + require.NoError(tb, err) return rule } -func setupFolderService(t *testing.T, sqlStore db.DB, cfg *setting.Cfg, features featuremgmt.FeatureToggles) folder.Service { +func setupFolderService(t testing.TB, sqlStore db.DB, cfg *setting.Cfg, features featuremgmt.FeatureToggles) folder.Service { tracer := tracing.InitializeTracerForTest() inProcBus := bus.ProvideBus(tracer) folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore) @@ -1735,6 +1735,169 @@ func TestIntegration_AlertRuleVersionsCleanup(t *testing.T) { }) } +func TestIntegration_ListAlertRulesByGroup(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + sqlStore := db.InitTestDB(t) + cfg := setting.NewCfg() + cfg.UnifiedAlerting = setting.UnifiedAlertingSettings{ + BaseInterval: time.Duration(rand.Int64N(100)+1) * time.Second, + } + folderService := setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures()) + bus := &fakeBus{} + orgID := int64(1) + ruleGen := models.RuleGen.With( + models.RuleMuts.WithIntervalMatching(cfg.UnifiedAlerting.BaseInterval), + models.RuleMuts.WithOrgID(orgID), + ) + store := createTestStore(sqlStore, folderService, &logtest.Fake{}, cfg.UnifiedAlerting, bus) + + // set test params + numFolders := 10 + numRules := 50 + rulesPerGroup := 5 + totalGroups := numRules / rulesPerGroup // 10 + + // create rules with different group names + rules, _ := createManyRules(t, + store, + ruleGen, + numFolders, + numRules, + rulesPerGroup, + ) + // sort rules by folder, then group, then group index + slices.SortStableFunc(rules, func(a, b *models.AlertRule) int { + if a.NamespaceUID != b.NamespaceUID { + return strings.Compare(a.NamespaceUID, b.NamespaceUID) + } + if a.RuleGroup != b.RuleGroup { + return strings.Compare(a.RuleGroup, b.RuleGroup) + } + return a.RuleGroupIndex - b.RuleGroupIndex + }) + + t.Run("should return all rules when no limit passed", func(t *testing.T) { + result, continueToken, err := store.ListAlertRulesByGroup(context.Background(), &models.ListAlertRulesByGroupQuery{ + OrgID: orgID, + }) + require.NoError(t, err) + require.Len(t, result, 50, "should return all rules when no limit is set") + require.Empty(t, continueToken, "continue token should be empty when no limit is set") + }) + + t.Run("should return paginated results when group limit is set", func(t *testing.T) { + // random number from 1 to totalGroups - 1 (to ensure we always receive less than totalGroups) + groupLimit := rand.Int64N(int64(totalGroups)-1) + 1 + result, continueToken, err := store.ListAlertRulesByGroup(context.Background(), &models.ListAlertRulesByGroupQuery{ + OrgID: orgID, + GroupLimit: groupLimit, + }) + require.NoError(t, err) + expectedRuleCount := groupLimit * int64(rulesPerGroup) + require.Len(t, result, int(expectedRuleCount), fmt.Sprintf("should return %d rules when group limit is set", expectedRuleCount)) + require.NotEmpty(t, continueToken, "continue token should not be empty when limit is set") + }) + + t.Run("pagination should all for continuation", func(t *testing.T) { + groupLimit := int64(2) // fixed group limit for this test + result, continueToken, err := store.ListAlertRulesByGroup(context.Background(), &models.ListAlertRulesByGroupQuery{ + OrgID: orgID, + GroupLimit: groupLimit, + }) + require.NoError(t, err) + require.Len(t, result, int(groupLimit*int64(rulesPerGroup)), "should return rules for the first two groups") + require.NotEmpty(t, continueToken, "continue token should not be empty") + + for i, rule := range result { + expected := rules[i].RuleGroup + actual := rule.RuleGroup + require.Equal(t, expected, actual, "rules should be ordered by group name") + } + + resultRules := make([]*models.AlertRule, 0, len(result)) + resultRules = append(resultRules, result...) + + // Continue from previous, fetching the rest of the rules + result, continueToken, err = store.ListAlertRulesByGroup(context.Background(), &models.ListAlertRulesByGroupQuery{ + OrgID: orgID, + GroupContinueToken: continueToken, + }) + require.NoError(t, err) + resultRules = append(resultRules, result...) + require.Len(t, resultRules, numRules, "should return all rules when continuing from the last token") + require.Empty(t, continueToken, "continue token should be empty when all rules are fetched") + for i, rule := range resultRules { + expected := rules[i].RuleGroup + actual := rule.RuleGroup + require.Equal(t, expected, actual, "rules should be ordered by group name") + } + }) +} + +func Benchmark_ListAlertRules(b *testing.B) { + orgID := int64(1) + ruleGen := models.RuleGen + + // init + sqlStore := db.InitTestDB(b) + cfg := setting.NewCfg() + cfg.UnifiedAlerting = setting.UnifiedAlertingSettings{ + BaseInterval: time.Duration(rand.Int64N(100)) * time.Second, + } + folderService := setupFolderService(b, sqlStore, cfg, featuremgmt.WithFeatures()) + bus := &fakeBus{} + store := createTestStore(sqlStore, folderService, &logtest.Fake{}, cfg.UnifiedAlerting, bus) + + ruleGen = ruleGen.With( + ruleGen.WithIntervalMatching(cfg.UnifiedAlerting.BaseInterval), + ruleGen.WithOrgID(orgID), + ) + + // define benchmark parameters + numFolders := 5 + numRules := 10000 + rulesPerGroup := 100 + assert.Greater(b, numRules, rulesPerGroup, "n must be greater than rulesPerGroup") + assert.Equal(b, 0, numRules%rulesPerGroup, "n % rulesPerGroup must be zero to create equal groups") + + // create rules and folders (5 folders, each with n/rulesPerGroup rules) + _, _ = createManyRules(b, + store, + ruleGen, + numFolders, // number of folders + numRules, // total number of rules + rulesPerGroup, // rules per group + ) + + b.Run(fmt.Sprintf("list %d rules unpaginated", numRules), func(b *testing.B) { + for b.Loop() { + _, err := store.ListAlertRules(context.Background(), &models.ListAlertRulesQuery{ + OrgID: orgID, + }) + if err != nil { + b.Fatal(err) + } + } + }) + + for _, groupLimit := range []int{1, 2, 5, 10, 50, 100} { + b.Run(fmt.Sprintf("list %d groups paginated", groupLimit), func(b *testing.B) { + for b.Loop() { + _, _, err := store.ListAlertRulesByGroup(context.Background(), &models.ListAlertRulesByGroupQuery{ + OrgID: orgID, + GroupLimit: int64(groupLimit), + }) + if err != nil { + b.Fatal(err) + } + } + }) + } +} + func TestIntegration_ListAlertRules(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") @@ -1975,3 +2138,27 @@ func (f *fakeBus) Publish(ctx context.Context, msg bus.Msg) error { return nil } + +func createManyRules(tb testing.TB, store *DBstore, ruleGen *models.AlertRuleGenerator, numFolders, numRules, rulesPerGroup int) ([]*models.AlertRule, []string) { + tb.Helper() + + require.Greater(tb, numRules, 0, "numRules must be greater than 0") + require.Greater(tb, numFolders, 0, "numFolders must be greater than 0") + require.Greater(tb, numRules, rulesPerGroup, "numRules must be greater than rulesPerGroup") + require.Greater(tb, rulesPerGroup, 0, "rulesPerGroup must be greater than 0") + require.Equal(tb, numRules%rulesPerGroup, 0, "numRules % rulesPerGroup must be zero to create equal groups") + + rules := make([]*models.AlertRule, 0, numRules) + namespaceUIDs := make([]string, numFolders) + for i := range namespaceUIDs { + namespaceUIDs[i] = fmt.Sprintf("ns-%d", i) + } + for i := 0; i < numRules; i++ { + gen := ruleGen.With( + ruleGen.WithNamespaceUID(namespaceUIDs[i%numFolders]), + ruleGen.WithGroupName(fmt.Sprintf("group_%d", i%(numRules/rulesPerGroup))), + ) + rules = append(rules, createRule(tb, store, gen)) + } + return rules, namespaceUIDs +} diff --git a/pkg/services/ngalert/tests/fakes/rules.go b/pkg/services/ngalert/tests/fakes/rules.go index 046522f3214..c17c70bf6c9 100644 --- a/pkg/services/ngalert/tests/fakes/rules.go +++ b/pkg/services/ngalert/tests/fakes/rules.go @@ -4,6 +4,7 @@ import ( "context" "math/rand" "slices" + "strings" "sync" "testing" "time" @@ -185,6 +186,88 @@ func (f *RuleStore) GetAlertRulesGroupByRuleUID(_ context.Context, q *models.Get return ruleList, nil } +func (f *RuleStore) ListAlertRulesByGroup(_ context.Context, q *models.ListAlertRulesByGroupQuery) (models.RulesGroup, string, error) { + f.mtx.Lock() + defer f.mtx.Unlock() + f.RecordedOps = append(f.RecordedOps, *q) + + if err := f.Hook(*q); err != nil { + return nil, "", err + } + + query := &models.ListAlertRulesQuery{ + OrgID: q.OrgID, + NamespaceUIDs: q.NamespaceUIDs, + DashboardUID: q.DashboardUID, + PanelID: q.PanelID, + RuleGroups: q.RuleGroups, + RuleUIDs: q.RuleUIDs, + ReceiverName: q.ReceiverName, + HasPrometheusRuleDefinition: q.HasPrometheusRuleDefinition, + } + + ruleList, err := f.listAlertRules(query) + if err != nil { + return nil, "", err + } + + // < group limit logic > + + // sort rules to ensure order is consistent, pagination depends on this + slices.SortFunc(ruleList, func(a, b *models.AlertRule) int { + nsCmp := strings.Compare(a.NamespaceUID, b.NamespaceUID) + if nsCmp != 0 { + return nsCmp + } + rgCmp := strings.Compare(a.RuleGroup, b.RuleGroup) + if rgCmp != 0 { + return rgCmp + } + return models.RulesGroupComparer(a, b) + }) + + var nextToken string + var cursor models.GroupCursor + if q.GroupContinueToken != "" { + if cur, err := models.DecodeGroupCursor(q.GroupContinueToken); err == nil { + cursor = cur + } + } + + if q.GroupLimit < 0 { + return ruleList, "", nil + } + + outputRules := make([]*models.AlertRule, 0, len(ruleList)) + var groupsFetched int64 + initialCursor := cursor + for _, r := range ruleList { + // skip rules before the initial cursor + if initialCursor.NamespaceUID != "" && + (strings.Compare(r.NamespaceUID, initialCursor.NamespaceUID) < 0 || + (strings.Compare(r.NamespaceUID, initialCursor.NamespaceUID) == 0 && strings.Compare(r.RuleGroup, initialCursor.RuleGroup) <= 0)) { + continue + } + + key := models.GroupCursor{ + NamespaceUID: r.NamespaceUID, + RuleGroup: r.RuleGroup, + } + if key != cursor { + if q.GroupLimit > 0 && groupsFetched == q.GroupLimit { + nextToken = models.EncodeGroupCursor(cursor) + break + } + cursor = key + groupsFetched++ + } + + outputRules = append(outputRules, r) + } + + return outputRules, nextToken, nil +} + func (f *RuleStore) ListAlertRules(_ context.Context, q *models.ListAlertRulesQuery) (models.RulesGroup, error) { f.mtx.Lock() defer f.mtx.Unlock() @@ -194,6 +277,10 @@ func (f *RuleStore) ListAlertRules(_ context.Context, q *models.ListAlertRulesQu return nil, err } + return f.listAlertRules(q) +} + +func (f *RuleStore) listAlertRules(q *models.ListAlertRulesQuery) (models.RulesGroup, error) { hasDashboard := func(r *models.AlertRule, dashboardUID string, panelID int64) bool { if dashboardUID != "" { if r.DashboardUID == nil || *r.DashboardUID != dashboardUID { diff --git a/pkg/services/query/query.go b/pkg/services/query/query.go index 73612d41f22..7aaafdea105 100644 --- a/pkg/services/query/query.go +++ b/pkg/services/query/query.go @@ -2,7 +2,6 @@ package query import ( "context" - "encoding/json" "errors" "fmt" "net/http" @@ -14,7 +13,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/gtime" "golang.org/x/sync/errgroup" - data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -88,6 +86,7 @@ type ServiceImpl struct { concurrentQueryLimit int mtDatasourceClientBuilder mtdsclient.MTDatasourceClientBuilder headers map[string]string + supportLocalTimeRange bool } // Run ServiceImpl. @@ -105,7 +104,7 @@ func (s *ServiceImpl) QueryData(ctx context.Context, user identity.Requester, sk } } // Parse the request into parsed queries grouped by datasource uid - parsedReq, err := s.parseMetricRequest(ctx, user, skipDSCache, reqDTO) + parsedReq, err := s.parseMetricRequest(ctx, user, skipDSCache, reqDTO, s.supportLocalTimeRange) if err != nil { return nil, err } @@ -224,6 +223,7 @@ func QueryData(ctx context.Context, log log.Logger, dscache datasources.CacheSer mtDatasourceClientBuilder: mtDatasourceClientBuilder, headers: headers, concurrentQueryLimit: 16, // TODO: make it configurable + supportLocalTimeRange: true, } return s.QueryData(ctx, nil, false, reqDTO) } @@ -303,32 +303,32 @@ func (s *ServiceImpl) handleQuerySingleDatasource(ctx context.Context, user iden return s.pluginClient.QueryData(ctx, req) } else { // multi tenant flow // transform request from backend.QueryDataRequest to k8s request - k8sReq := &data.QueryDataRequest{ - TimeRange: data.TimeRange{ - From: req.Queries[0].TimeRange.From.Format(time.RFC3339), - To: req.Queries[0].TimeRange.To.Format(time.RFC3339), - }, - } - for _, q := range req.Queries { - var dataQuery data.DataQuery - err := json.Unmarshal(q.JSON, &dataQuery) - if err != nil { - return nil, err - } - - k8sReq.Queries = append(k8sReq.Queries, dataQuery) + k8sReq, err := expr.ConvertBackendRequestToDataRequest(req) + if err != nil { + return nil, err } return mtDsClient.QueryData(ctx, *k8sReq) } } +func getTimeRange(query *simplejson.Json, globalFrom string, globalTo string) gtime.TimeRange { + from := query.Get("timeRange").Get("from").MustString("") + to := query.Get("timeRange").Get("to").MustString("") + + if (from == "") && (to == "") { + from = globalFrom + to = globalTo + } + + return gtime.NewTimeRange(from, to) +} + // parseRequest parses a request into parsed queries grouped by datasource uid -func (s *ServiceImpl) parseMetricRequest(ctx context.Context, user identity.Requester, skipDSCache bool, reqDTO dtos.MetricRequest) (*parsedRequest, error) { +func (s *ServiceImpl) parseMetricRequest(ctx context.Context, user identity.Requester, skipDSCache bool, reqDTO dtos.MetricRequest, supportLocalTimeRange bool) (*parsedRequest, error) { if len(reqDTO.Queries) == 0 { return nil, ErrNoQueriesFound } - timeRange := gtime.NewTimeRange(reqDTO.From, reqDTO.To) req := &parsedRequest{ hasExpression: false, parsedQueries: make(map[string][]parsedQuery), @@ -357,6 +357,13 @@ func (s *ServiceImpl) parseMetricRequest(ctx context.Context, user identity.Requ req.parsedQueries[ds.UID] = []parsedQuery{} } + var timeRange gtime.TimeRange + if supportLocalTimeRange { + timeRange = getTimeRange(query, reqDTO.From, reqDTO.To) + } else { + timeRange = gtime.NewTimeRange(reqDTO.From, reqDTO.To) + } + modelJSON, err := query.MarshalJSON() if err != nil { return nil, err diff --git a/pkg/services/query/query_test.go b/pkg/services/query/query_test.go index 66c4ff17f85..7187312da50 100644 --- a/pkg/services/query/query_test.go +++ b/pkg/services/query/query_test.go @@ -69,7 +69,7 @@ func TestIntegrationParseMetricRequest(t *testing.T) { "type": "postgres" } }`) - parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr) + parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr, false) require.NoError(t, err) require.NotNil(t, parsedReq) assert.False(t, parsedReq.hasExpression) @@ -95,7 +95,7 @@ func TestIntegrationParseMetricRequest(t *testing.T) { }`) mr.From = "" mr.To = "" - parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr) + parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr, false) require.NoError(t, err) require.NotNil(t, parsedReq) assert.False(t, parsedReq.hasExpression) @@ -128,7 +128,7 @@ func TestIntegrationParseMetricRequest(t *testing.T) { "type": "math", "expression": "$A - 50" }`) - parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr) + parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr, false) require.NoError(t, err) require.NotNil(t, parsedReq) require.True(t, parsedReq.hasExpression) @@ -171,7 +171,7 @@ func TestIntegrationParseMetricRequest(t *testing.T) { "type": "testdata" } }`) - parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr) + parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr, false) require.NoError(t, err) require.NotNil(t, parsedReq) assert.False(t, parsedReq.hasExpression) @@ -231,7 +231,7 @@ func TestIntegrationParseMetricRequest(t *testing.T) { "type": "math", "expression": "$A_resample + $B_resample" }`) - parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr) + parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr, false) require.NoError(t, err) require.NotNil(t, parsedReq) assert.True(t, parsedReq.hasExpression) @@ -271,18 +271,18 @@ func TestIntegrationParseMetricRequest(t *testing.T) { reqCtx.Req = httpreq httpreq.Header.Add("X-Datasource-Uid", "gIEkMvIVz") - _, err = tc.queryService.parseMetricRequest(httpreq.Context(), tc.signedInUser, true, mr) + _, err = tc.queryService.parseMetricRequest(httpreq.Context(), tc.signedInUser, true, mr, false) require.NoError(t, err) // With the second value it is OK httpreq.Header.Add("X-Datasource-Uid", "sEx6ZvSVk") - _, err = tc.queryService.parseMetricRequest(httpreq.Context(), tc.signedInUser, true, mr) + _, err = tc.queryService.parseMetricRequest(httpreq.Context(), tc.signedInUser, true, mr, false) require.NoError(t, err) // Single header with comma syntax httpreq, _ = http.NewRequest(http.MethodPost, "http://localhost/", bytes.NewReader([]byte{})) httpreq.Header.Set("X-Datasource-Uid", "gIEkMvIVz, sEx6ZvSVk") - _, err = tc.queryService.parseMetricRequest(httpreq.Context(), tc.signedInUser, true, mr) + _, err = tc.queryService.parseMetricRequest(httpreq.Context(), tc.signedInUser, true, mr, false) require.NoError(t, err) }) @@ -301,9 +301,132 @@ func TestIntegrationParseMetricRequest(t *testing.T) { "type": "postgres" } }`) - _, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr) + _, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr, false) require.Error(t, err) }) + + t.Run("Test a datasource query with global time range", func(t *testing.T) { + tc := setup(t, false, nil) + mr := metricRequestWithQueries(t, `{ + "refId": "A", + "datasource": { + "uid": "gIEkMvIVz", + "type": "postgres" + } + }`, `{ + "refId": "B", + "datasource": { + "uid": "gIEkMvIVz", + "type": "postgres" + } + }`) + mr.From = "1753944628000" + mr.To = "1753944629000" + parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr, false) + require.NoError(t, err) + require.NotNil(t, parsedReq) + assert.Len(t, parsedReq.parsedQueries, 1) + assert.Contains(t, parsedReq.parsedQueries, "gIEkMvIVz") + queries := parsedReq.getFlattenedQueries() + assert.Len(t, queries, 2) + for _, q := range queries { + assert.Equal(t, int64(1753944628000), q.query.TimeRange.From.UnixMilli()) + assert.Equal(t, int64(1753944629000), q.query.TimeRange.To.UnixMilli()) + } + }) + t.Run("Test a datasource query with local time range", func(t *testing.T) { + tc := setup(t, false, nil) + mr := metricRequestWithQueries(t, `{ + "refId": "A", + "datasource": { + "uid": "gIEkMvIVz", + "type": "postgres" + }, + "timeRange": { + "from": "1753944618000", + "to": "1753944619000" + } + }`, `{ + "refId": "B", + "datasource": { + "uid": "gIEkMvIVz", + "type": "postgres" + }, + "timeRange": { + "from": "1753944628000", + "to": "1753944629000" + } + }`) + mr.From = "" + mr.To = "" + + verifyTimestamps := func(parsedReq *parsedRequest, ts1 int64, ts2 int64, ts3 int64, ts4 int64) { + require.NotNil(t, parsedReq) + assert.Len(t, parsedReq.parsedQueries, 1) + assert.Contains(t, parsedReq.parsedQueries, "gIEkMvIVz") + queries := parsedReq.getFlattenedQueries() + assert.Len(t, queries, 2) + + assert.Equal(t, ts1, queries[0].query.TimeRange.From.UnixMilli()) + assert.Equal(t, ts2, queries[0].query.TimeRange.To.UnixMilli()) + + assert.Equal(t, ts3, queries[1].query.TimeRange.From.UnixMilli()) + assert.Equal(t, ts4, queries[1].query.TimeRange.To.UnixMilli()) + } + + // with flag enabled + parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr, true) + require.NoError(t, err) + + verifyTimestamps(parsedReq, + int64(1753944618000), + int64(1753944619000), + int64(1753944628000), + int64(1753944629000)) + + // with flag disabled + parsedReq2, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr, false) + require.NoError(t, err) + + verifyTimestamps(parsedReq2, int64(0), int64(0), int64(0), int64(0)) + }) + + t.Run("Test a datasource query with malformed local time range", func(t *testing.T) { + tc := setup(t, false, nil) + mr := metricRequestWithQueries(t, `{ + "refId": "A", + "datasource": { + "uid": "gIEkMvIVz", + "type": "postgres" + }, + "timeRange": { + "from": "1753944618000", + "not_to": "1753944619000" + } + }`, `{ + "refId": "B", + "datasource": { + "uid": "gIEkMvIVz", + "type": "postgres" + }, + "timeRange": 42 + }`) + mr.From = "" + mr.To = "" + parsedReq, err := tc.queryService.parseMetricRequest(context.Background(), tc.signedInUser, true, mr, true) + require.NoError(t, err) + require.NotNil(t, parsedReq) + assert.Len(t, parsedReq.parsedQueries, 1) + assert.Contains(t, parsedReq.parsedQueries, "gIEkMvIVz") + queries := parsedReq.getFlattenedQueries() + assert.Len(t, queries, 2) + + assert.Equal(t, int64(1753944618000), queries[0].query.TimeRange.From.UnixMilli()) + assert.Equal(t, int64(0), queries[0].query.TimeRange.To.UnixMilli()) + + assert.Equal(t, int64(0), queries[1].query.TimeRange.From.UnixMilli()) + assert.Equal(t, int64(0), queries[1].query.TimeRange.To.UnixMilli()) + }) } func TestIntegrationQueryDataMultipleSources(t *testing.T) { @@ -515,17 +638,13 @@ func TestIntegrationQueryDataWithMTDSClient(t *testing.T) { "type": "postgres" } }`) - mr.From = "2022-01-01" - mr.To = "2022-01-02" + mr.From = "1754309340000" + mr.To = "1754309370000" ctx := context.Background() _, err := tc.queryService.QueryData(ctx, tc.signedInUser, true, mr) require.NoError(t, err) assert.Equal(t, data.QueryDataRequest{ - TimeRange: data.TimeRange{ - From: "2022-01-01T00:00:00Z", - To: "2022-01-02T00:00:00Z", - }, Queries: []data.DataQuery{ { CommonQueryProperties: data.CommonQueryProperties{ @@ -534,6 +653,12 @@ func TestIntegrationQueryDataWithMTDSClient(t *testing.T) { Type: "postgres", UID: "gIEkMvIVz", }, + TimeRange: &data.TimeRange{ + From: "1754309340000", + To: "1754309370000", + }, + MaxDataPoints: 100, + IntervalMS: 1000, }, }, { @@ -543,6 +668,12 @@ func TestIntegrationQueryDataWithMTDSClient(t *testing.T) { Type: "postgres", UID: "gIEkMvIVz", }, + TimeRange: &data.TimeRange{ + From: "1754309340000", + To: "1754309370000", + }, + MaxDataPoints: 100, + IntervalMS: 1000, }, }, }, diff --git a/pkg/services/sqlstore/database_config.go b/pkg/services/sqlstore/database_config.go index f5ba069df15..47e77640aa8 100644 --- a/pkg/services/sqlstore/database_config.go +++ b/pkg/services/sqlstore/database_config.go @@ -43,6 +43,7 @@ type DatabaseConfig struct { MigrationLock bool MigrationLockAttemptTimeout int LogQueries bool + DeleteAutoGenIDs bool // SQLite only QueryRetries int // SQLite only @@ -123,6 +124,7 @@ func (dbCfg *DatabaseConfig) readConfig(cfg *setting.Cfg) error { dbCfg.TransactionRetries = sec.Key("transaction_retries").MustInt(5) dbCfg.LogQueries = sec.Key("log_queries").MustBool(false) + dbCfg.DeleteAutoGenIDs = sec.Key("delete_auto_gen_ids").MustBool(false) return nil } diff --git a/pkg/services/store/kind/dashboard/testdata/gdev-walk-graph-time-regions.json b/pkg/services/store/kind/dashboard/testdata/gdev-walk-graph-time-regions.json index 40e15af1cf1..a5ece77a66a 100644 --- a/pkg/services/store/kind/dashboard/testdata/gdev-walk-graph-time-regions.json +++ b/pkg/services/store/kind/dashboard/testdata/gdev-walk-graph-time-regions.json @@ -18,8 +18,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -37,8 +36,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -56,8 +54,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -75,8 +72,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -94,8 +90,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -107,8 +102,7 @@ ], "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", diff --git a/pkg/services/store/kind/dashboard/testdata/gdev-walk-graph_tests.json b/pkg/services/store/kind/dashboard/testdata/gdev-walk-graph_tests.json index 44cece55fe9..017a344964e 100644 --- a/pkg/services/store/kind/dashboard/testdata/gdev-walk-graph_tests.json +++ b/pkg/services/store/kind/dashboard/testdata/gdev-walk-graph_tests.json @@ -18,8 +18,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -37,8 +36,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -56,8 +54,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -75,8 +72,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -111,8 +107,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -147,8 +142,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -166,8 +160,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -202,8 +195,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -238,8 +230,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -274,8 +265,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -310,8 +300,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -329,8 +318,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -348,8 +336,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -367,8 +354,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -386,8 +372,7 @@ }, "references": [ { - "family": "ds", - "identifier": "gdev-testdata" + "family": "ds" }, { "family": "plugin", @@ -401,10 +386,6 @@ { "family": "ds" }, - { - "family": "ds", - "identifier": "gdev-testdata" - }, { "family": "plugin", "type": "panel", diff --git a/pkg/storage/secret/metadata/secure_value_test.go b/pkg/storage/secret/metadata/secure_value_test.go index 9400735cdd1..7f387847848 100644 --- a/pkg/storage/secret/metadata/secure_value_test.go +++ b/pkg/storage/secret/metadata/secure_value_test.go @@ -403,9 +403,8 @@ func TestStateMachine(t *testing.T) { }, "decrypt": func(t *rapid.T) { input := decryptGen.Draw(t, "decryptInput") - authCtx := testutils.CreateServiceAuthContext(t.Context(), input.decrypter, []string{fmt.Sprintf("secret.grafana.app/securevalues/%+v:decrypt", input.name)}) modelResult, modelErr := model.decrypt(input.decrypter, input.namespace, input.name) - result, err := sut.DecryptService.Decrypt(authCtx, input.namespace, input.name) + result, err := sut.DecryptService.Decrypt(t.Context(), input.decrypter, input.namespace, []string{input.name}) if err != nil || modelErr != nil { require.ErrorIs(t, err, modelErr) return @@ -440,8 +439,7 @@ func TestSecureValueServiceExampleBased(t *testing.T) { require.NoError(t, err) require.Equal(t, sv.Status.Version, deletedSv.Status.Version) - authCtx := testutils.CreateServiceAuthContext(t.Context(), sv.Spec.Decrypters[0], []string{fmt.Sprintf("secret.grafana.app/securevalues/%+v:decrypt", sv.Name)}) - result, err := sut.DecryptService.Decrypt(authCtx, sv.Namespace, sv.Name) + result, err := sut.DecryptService.Decrypt(t.Context(), sv.Spec.Decrypters[0], sv.Namespace, []string{sv.Name}) require.NoError(t, err) require.Equal(t, 1, len(result)) require.ErrorIs(t, result[sv.Name].Error(), contracts.ErrDecryptNotFound) diff --git a/pkg/storage/unified/README.md b/pkg/storage/unified/README.md index dd53fa622b0..608f51c062d 100644 --- a/pkg/storage/unified/README.md +++ b/pkg/storage/unified/README.md @@ -367,8 +367,6 @@ signing_keys_url = http://localhost:3011/api/signing-keys/keys mode = "on-prem" [feature_toggles] -kubernetesDashboardsAPI = true -kubernetesFolders = true unifiedStorage = true unifiedStorageSearch = true @@ -415,8 +413,6 @@ http_port = 3011 http_addr = "127.0.0.2" [feature_toggles] -kubernetesDashboardsAPI = true -kubernetesFolders = true unifiedStorageSearchUI = true [unified_storage.dashboards.dashboard.grafana.app] diff --git a/pkg/storage/unified/resource/search_client.go b/pkg/storage/unified/resource/search_client.go index e0e36bcb51c..4f6ecc76902 100644 --- a/pkg/storage/unified/resource/search_client.go +++ b/pkg/storage/unified/resource/search_client.go @@ -2,8 +2,11 @@ package resource import ( "context" + "fmt" "time" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" "google.golang.org/grpc" "k8s.io/apimachinery/pkg/runtime/schema" @@ -17,6 +20,20 @@ const ( backgroundRequestTimeout = 500 * time.Millisecond ) +var ( + // searchResultsMatchHistogram tracks the percentage match between legacy and unified search results + searchResultsMatchHistogram = promauto.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "grafana", + Subsystem: "unified_storage", + Name: "search_results_match_percentage", + Help: "Histogram of percentage match between legacy and unified search results", + Buckets: []float64{0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, + }, + []string{"resource_type"}, + ) +) + type DualWriter interface { IsEnabled(schema.GroupResource) bool ReadFromUnified(context.Context, schema.GroupResource) (bool, error) @@ -44,6 +61,43 @@ type searchWrapper struct { logger log.Logger } +// extractUIDs extracts unique UIDs from search response results +func extractUIDs(response *resourcepb.ResourceSearchResponse) map[string]struct{} { + uids := make(map[string]struct{}) + if response == nil || response.Results == nil || response.Results.Rows == nil { + return uids + } + + for _, row := range response.Results.Rows { + if row.Key != nil && row.Key.Name != "" { + uids[row.Key.Name] = struct{}{} + } + } + return uids +} + +// calculateMatchPercentage calculates recall: what percentage of legacy results were also found by unified search +func calculateMatchPercentage(legacyUIDs, unifiedUIDs map[string]struct{}) float64 { + if len(legacyUIDs) == 0 && len(unifiedUIDs) == 0 { + return 100.0 // Both empty, consider as 100% match + } + if len(legacyUIDs) == 0 || len(unifiedUIDs) == 0 { + return 0.0 // One empty, other not + } + + // Count matches: how many legacy results did unified also return? + matches := 0 + for uid := range legacyUIDs { + if _, exists := unifiedUIDs[uid]; exists { + matches++ + } + } + + // Calculate recall: percentage of legacy results that unified also returned + // Legacy is the source of truth, so we use it as the denominator + return float64(matches) / float64(len(legacyUIDs)) * 100.0 +} + func (s *searchWrapper) GetStats(ctx context.Context, in *resourcepb.ResourceStatsRequest, opts ...grpc.CallOption) (*resourcepb.ResourceStatsResponse, error) { client := s.legacyClient @@ -89,23 +143,67 @@ func (s *searchWrapper) Search(ctx context.Context, in *resourcepb.ResourceSearc } // If dual reader feature flag is enabled, and legacy is the main storage, - // make a background call to unified + // make a background call to unified and compare results if s.features != nil && s.features.IsEnabledGlobally(featuremgmt.FlagUnifiedStorageSearchDualReaderEnabled) && !unified { + // Get the legacy result first + legacyResponse, legacyErr := s.legacyClient.Search(ctx, in, opts...) + if legacyErr != nil { + return nil, legacyErr + } + // Create background context with timeout but ignore parent cancelation ctxBg := context.WithoutCancel(ctx) ctxBgWithTimeout, cancel := context.WithTimeout(ctxBg, backgroundRequestTimeout) - // Make background call without blocking the main request + // Make background call and compare results go func() { defer cancel() // Ensure we clean up the context - _, bgErr := s.unifiedClient.Search(ctxBgWithTimeout, in, opts...) + unifiedResponse, bgErr := s.unifiedClient.Search(ctxBgWithTimeout, in, opts...) if bgErr != nil { s.logger.Error("Background Search call to unified failed", "error", bgErr, "timeout", backgroundRequestTimeout) } else { s.logger.Debug("Background Search call to unified succeeded") + + // Compare results when both are successful + var requestKey *resourcepb.ResourceKey + if in.Options != nil { + requestKey = in.Options.Key + } + s.compareSearchResults(legacyResponse, unifiedResponse, requestKey) } }() + + return legacyResponse, nil } return client.Search(ctx, in, opts...) } + +// compareSearchResults compares legacy and unified search results and logs/metrics the outcome +func (s *searchWrapper) compareSearchResults(legacyResponse, unifiedResponse *resourcepb.ResourceSearchResponse, requestKey *resourcepb.ResourceKey) { + if legacyResponse == nil || unifiedResponse == nil { + return + } + + legacyUIDs := extractUIDs(legacyResponse) + unifiedUIDs := extractUIDs(unifiedResponse) + + matchPercentage := calculateMatchPercentage(legacyUIDs, unifiedUIDs) + + // Determine resource type for labeling - handle nil safely + resourceType := "unknown" + if requestKey != nil && requestKey.Resource != "" { + resourceType = requestKey.Resource + } + + s.logger.Debug("Search results comparison completed", + "resource_type", resourceType, + "legacy_count", len(legacyUIDs), + "unified_count", len(unifiedUIDs), + "match_percentage", fmt.Sprintf("%.1f%%", matchPercentage), + "legacy_total_hits", legacyResponse.TotalHits, + "unified_total_hits", unifiedResponse.TotalHits, + ) + + searchResultsMatchHistogram.WithLabelValues(resourceType).Observe(matchPercentage) +} diff --git a/pkg/storage/unified/resource/search_client_test.go b/pkg/storage/unified/resource/search_client_test.go index 7784dc52e16..927f9e9e60e 100644 --- a/pkg/storage/unified/resource/search_client_test.go +++ b/pkg/storage/unified/resource/search_client_test.go @@ -418,3 +418,127 @@ func TestSearchWrapper_GetStats(t *testing.T) { unifiedClient.AssertExpectations(t) }) } + +func TestExtractUIDs(t *testing.T) { + tests := []struct { + name string + response *resourcepb.ResourceSearchResponse + expected map[string]struct{} + }{ + { + name: "nil response", + response: nil, + expected: map[string]struct{}{}, + }, + { + name: "empty results", + response: &resourcepb.ResourceSearchResponse{ + Results: &resourcepb.ResourceTable{ + Rows: []*resourcepb.ResourceTableRow{}, + }, + }, + expected: map[string]struct{}{}, + }, + { + name: "single result", + response: &resourcepb.ResourceSearchResponse{ + Results: &resourcepb.ResourceTable{ + Rows: []*resourcepb.ResourceTableRow{ + { + Key: &resourcepb.ResourceKey{ + Name: "test-uid-1", + }, + }, + }, + }, + }, + expected: map[string]struct{}{"test-uid-1": {}}, + }, + { + name: "multiple results", + response: &resourcepb.ResourceSearchResponse{ + Results: &resourcepb.ResourceTable{ + Rows: []*resourcepb.ResourceTableRow{ + { + Key: &resourcepb.ResourceKey{ + Name: "test-uid-1", + }, + }, + { + Key: &resourcepb.ResourceKey{ + Name: "test-uid-2", + }, + }, + }, + }, + }, + expected: map[string]struct{}{"test-uid-1": {}, "test-uid-2": {}}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := extractUIDs(tt.response) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestCalculateMatchPercentage(t *testing.T) { + tests := []struct { + name string + legacyUIDs map[string]struct{} + unifiedUIDs map[string]struct{} + expected float64 + }{ + { + name: "both empty", + legacyUIDs: map[string]struct{}{}, + unifiedUIDs: map[string]struct{}{}, + expected: 100.0, + }, + { + name: "legacy empty, unified has results", + legacyUIDs: map[string]struct{}{}, + unifiedUIDs: map[string]struct{}{"uid1": {}}, + expected: 0.0, + }, + { + name: "legacy has results, unified empty", + legacyUIDs: map[string]struct{}{"uid1": {}}, + unifiedUIDs: map[string]struct{}{}, + expected: 0.0, + }, + { + name: "perfect match", + legacyUIDs: map[string]struct{}{"uid1": {}, "uid2": {}}, + unifiedUIDs: map[string]struct{}{"uid1": {}, "uid2": {}}, + expected: 100.0, + }, + { + name: "partial match", + legacyUIDs: map[string]struct{}{"uid1": {}, "uid2": {}}, + unifiedUIDs: map[string]struct{}{"uid1": {}, "uid3": {}}, + expected: 50.0, // 1 match out of 2 legacy UIDs (recall) + }, + { + name: "no match", + legacyUIDs: map[string]struct{}{"uid1": {}, "uid2": {}}, + unifiedUIDs: map[string]struct{}{"uid3": {}, "uid4": {}}, + expected: 0.0, + }, + { + name: "legacy subset of unified", + legacyUIDs: map[string]struct{}{"uid1": {}}, + unifiedUIDs: map[string]struct{}{"uid1": {}, "uid2": {}}, + expected: 100.0, // 1 match out of 1 legacy UID (perfect recall) + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := calculateMatchPercentage(tt.legacyUIDs, tt.unifiedUIDs) + assert.InDelta(t, tt.expected, result, 0.001) + }) + } +} diff --git a/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json b/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json index 4aa160ab2e9..ab1e77d03b9 100644 --- a/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/provisioning.grafana.app-v0alpha1.json @@ -49,27 +49,27 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobList" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobList" } }, "application/json;stream=watch": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobList" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobList" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobList" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobList" } }, "application/vnd.kubernetes.protobuf;stream=watch": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobList" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobList" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobList" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobList" } } } @@ -207,17 +207,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Job" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Job" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Job" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Job" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Job" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Job" } } } @@ -367,27 +367,27 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositoryList" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RepositoryList" } }, "application/json;stream=watch": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositoryList" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RepositoryList" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositoryList" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RepositoryList" } }, "application/vnd.kubernetes.protobuf;stream=watch": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositoryList" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RepositoryList" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositoryList" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RepositoryList" } } } @@ -439,17 +439,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } }, @@ -461,17 +461,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -481,17 +481,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -501,17 +501,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -713,17 +713,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -775,17 +775,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } }, @@ -797,17 +797,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -817,17 +817,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -1017,17 +1017,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -1037,17 +1037,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -1225,7 +1225,7 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceWrapper" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceWrapper" } } } @@ -1337,7 +1337,7 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceWrapper" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceWrapper" } } } @@ -1449,7 +1449,7 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceWrapper" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceWrapper" } } } @@ -1521,7 +1521,7 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceWrapper" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceWrapper" } } } @@ -2024,7 +2024,7 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceList" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceList" } } } @@ -2073,17 +2073,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -2135,17 +2135,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } }, @@ -2157,17 +2157,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -2177,17 +2177,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -2275,17 +2275,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -2295,17 +2295,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/vnd.kubernetes.protobuf": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } }, "application/yaml": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" } } } @@ -2392,7 +2392,7 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.TestResults" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.TestResults" } } } @@ -2441,7 +2441,7 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.WebhookResponse" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.WebhookResponse" } } } @@ -2466,7 +2466,7 @@ "content": { "*/*": { "schema": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.WebhookResponse" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.WebhookResponse" } } } @@ -2571,6 +2571,1432 @@ }, "components": { "schemas": { + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.BitbucketRepositoryConfig": { + "type": "object", + "required": [ + "branch" + ], + "properties": { + "branch": { + "description": "The branch to use in the repository.", + "type": "string", + "default": "" + }, + "encryptedToken": { + "description": "Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.", + "type": "string", + "format": "byte", + "x-kubernetes-list-type": "atomic" + }, + "path": { + "description": "Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. This is usually something like `grafana/`. Trailing and leading slash are not required. They are always added when needed. The path is relative to the root of the repository, regardless of the leading slash.\n\nWhen specifying something like `grafana-`, we will not look for `grafana-*`; we will only look for files under the directory `/grafana-/`. That means `/grafana-example.json` would not be found.", + "type": "string" + }, + "token": { + "description": "Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.", + "type": "string" + }, + "tokenUser": { + "description": "TokenUser is the user that will be used to access the repository if it's a personal access token.", + "type": "string" + }, + "url": { + "description": "The repository URL (e.g. `https://bitbucket.org/example/test`).", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.DeleteJobOptions": { + "type": "object", + "properties": { + "paths": { + "description": "Paths to be deleted. Examples: - dashboard.json (for a file) - a/b/c/other-dashboard.json (for a file) - nested/deep/ (for a directory) FIXME: we should validate this in admission hooks", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "ref": { + "description": "Ref to the branch or commit hash to delete from", + "type": "string" + }, + "resources": { + "description": "Resources to delete This option has been created because currently the frontend does not use standarized app platform APIs. For performance and API consistency reasons, the preferred option is it to use the paths.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceRef" + } + ] + } + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ErrorDetails": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "detail": { + "type": "string" + }, + "field": { + "type": "string" + }, + "type": { + "type": "string", + "default": "" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ExportJobOptions": { + "type": "object", + "properties": { + "branch": { + "description": "FIXME: we should validate this in admission hooks Target branch for export (only git)", + "type": "string" + }, + "folder": { + "description": "The source folder (or empty) to export", + "type": "string" + }, + "message": { + "description": "Message to use when committing the changes in a single commit", + "type": "string" + }, + "path": { + "description": "FIXME: we should validate this in admission hooks Prefix in target file system", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.GitHubRepositoryConfig": { + "type": "object", + "required": [ + "branch" + ], + "properties": { + "branch": { + "description": "The branch to use in the repository.", + "type": "string", + "default": "" + }, + "encryptedToken": { + "description": "Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.", + "type": "string", + "format": "byte", + "x-kubernetes-list-type": "atomic" + }, + "generateDashboardPreviews": { + "description": "Whether we should show dashboard previews for pull requests. By default, this is false (i.e. we will not create previews).", + "type": "boolean" + }, + "path": { + "description": "Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. This is usually something like `grafana/`. Trailing and leading slash are not required. They are always added when needed. The path is relative to the root of the repository, regardless of the leading slash.\n\nWhen specifying something like `grafana-`, we will not look for `grafana-*`; we will only look for files under the directory `/grafana-/`. That means `/grafana-example.json` would not be found.", + "type": "string" + }, + "token": { + "description": "Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.", + "type": "string" + }, + "url": { + "description": "The repository URL (e.g. `https://github.com/example/test`).", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.GitLabRepositoryConfig": { + "type": "object", + "required": [ + "branch" + ], + "properties": { + "branch": { + "description": "The branch to use in the repository.", + "type": "string", + "default": "" + }, + "encryptedToken": { + "description": "Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.", + "type": "string", + "format": "byte", + "x-kubernetes-list-type": "atomic" + }, + "path": { + "description": "Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. This is usually something like `grafana/`. Trailing and leading slash are not required. They are always added when needed. The path is relative to the root of the repository, regardless of the leading slash.\n\nWhen specifying something like `grafana-`, we will not look for `grafana-*`; we will only look for files under the directory `/grafana-/`. That means `/grafana-example.json` would not be found.", + "type": "string" + }, + "token": { + "description": "Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.", + "type": "string" + }, + "url": { + "description": "The repository URL (e.g. `https://gitlab.com/example/test`).", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.GitRepositoryConfig": { + "type": "object", + "required": [ + "branch" + ], + "properties": { + "branch": { + "description": "The branch to use in the repository.", + "type": "string", + "default": "" + }, + "encryptedToken": { + "description": "Token for accessing the repository, but encrypted. This is not possible to read back to a user decrypted.", + "type": "string", + "format": "byte", + "x-kubernetes-list-type": "atomic" + }, + "path": { + "description": "Path is the subdirectory for the Grafana data. If specified, Grafana will ignore anything that is outside this directory in the repository. This is usually something like `grafana/`. Trailing and leading slash are not required. They are always added when needed. The path is relative to the root of the repository, regardless of the leading slash.\n\nWhen specifying something like `grafana-`, we will not look for `grafana-*`; we will only look for files under the directory `/grafana-/`. That means `/grafana-example.json` would not be found.", + "type": "string" + }, + "token": { + "description": "Token for accessing the repository. If set, it will be encrypted into encryptedToken, then set to an empty string again.", + "type": "string" + }, + "tokenUser": { + "description": "TokenUser is the user that will be used to access the repository if it's a personal access token.", + "type": "string" + }, + "url": { + "description": "The repository URL (e.g. `https://github.com/example/test.git`).", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HealthStatus": { + "type": "object", + "required": [ + "healthy" + ], + "properties": { + "checked": { + "description": "When the health was checked last time", + "type": "integer", + "format": "int64" + }, + "healthy": { + "description": "When not healthy, requests will not be executed", + "type": "boolean", + "default": false + }, + "message": { + "description": "Summary messages (can be shown to users) Will only be populated when not healthy", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Job": { + "description": "The repository name and type are stored as labels", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + ] + }, + "spec": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobSpec" + } + ] + }, + "status": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobStatus" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "Job", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobList": { + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Job" + } + ] + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "JobList", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobResourceSummary": { + "type": "object", + "properties": { + "create": { + "type": "integer", + "format": "int64" + }, + "delete": { + "type": "integer", + "format": "int64" + }, + "error": { + "description": "Create or update (export)", + "type": "integer", + "format": "int64" + }, + "errors": { + "description": "Report errors for this resource type This may not be an exhaustive list and recommend looking at the logs for more info", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "group": { + "type": "string" + }, + "noop": { + "description": "No action required (useful for sync)", + "type": "integer", + "format": "int64" + }, + "resource": { + "type": "string" + }, + "total": { + "type": "integer", + "format": "int64" + }, + "update": { + "type": "integer", + "format": "int64" + }, + "write": { + "type": "integer", + "format": "int64" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobSpec": { + "type": "object", + "properties": { + "action": { + "description": "Possible enum values:\n - `\"delete\"` deletes files in the remote repository\n - `\"migrate\"` acts like JobActionExport, then JobActionPull. It also tries to preserve the history.\n - `\"move\"` moves files in the remote repository\n - `\"pr\"` adds additional useful information to a PR, such as comments with preview links and rendered images.\n - `\"pull\"` replicates the remote branch in the local copy of the repository.\n - `\"push\"` replicates the local copy of the repository in the remote branch.", + "type": "string", + "enum": [ + "delete", + "migrate", + "move", + "pr", + "pull", + "push" + ] + }, + "delete": { + "description": "Delete when the action is `delete`", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.DeleteJobOptions" + } + ] + }, + "migrate": { + "description": "Required when the action is `migrate`", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.MigrateJobOptions" + } + ] + }, + "move": { + "description": "Move when the action is `move`", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.MoveJobOptions" + } + ] + }, + "pr": { + "description": "Pull request options", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.PullRequestJobOptions" + } + ] + }, + "pull": { + "description": "Required when the action is `pull`", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.SyncJobOptions" + } + ] + }, + "push": { + "description": "Required when the action is `push`", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ExportJobOptions" + } + ] + }, + "repository": { + "description": "The the repository reference (for now also in labels) This value is required, but will be popuplated from the job making the request", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobStatus": { + "description": "The job status", + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "finished": { + "type": "integer", + "format": "int64" + }, + "message": { + "type": "string" + }, + "progress": { + "description": "Optional value 0-100 that can be set while running", + "type": "number", + "format": "double" + }, + "started": { + "type": "integer", + "format": "int64" + }, + "state": { + "description": "Possible enum values:\n - `\"error\"` Finished with errors\n - `\"pending\"` Job has been submitted, but not processed yet\n - `\"success\"` Finished with success\n - `\"warning\"` Finished with some non-critical errors\n - `\"working\"` The job is running", + "type": "string", + "enum": [ + "error", + "pending", + "success", + "warning", + "working" + ] + }, + "summary": { + "description": "Summary of processed actions", + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobResourceSummary" + } + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.LocalRepositoryConfig": { + "type": "object", + "properties": { + "path": { + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.MigrateJobOptions": { + "type": "object", + "properties": { + "history": { + "description": "Preserve history (if possible)", + "type": "boolean" + }, + "message": { + "description": "Message to use when committing the changes in a single commit", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.MoveJobOptions": { + "type": "object", + "properties": { + "paths": { + "description": "Paths to be deleted. Examples: - dashboard.json (for a file) - a/b/c/other-dashboard.json (for a file) - nested/deep/ (for a directory) FIXME: we should validate this in admission hooks", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "ref": { + "description": "Ref to the branch or commit hash that should move", + "type": "string" + }, + "resources": { + "description": "Resources to move This option has been created because currently the frontend does not use standarized app platform APIs. For performance and API consistency reasons, the preferred option is it to use the paths.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceRef" + } + ] + } + }, + "targetPath": { + "description": "Destination path for the move (e.g. \"new-location/\")", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.PullRequestJobOptions": { + "type": "object", + "properties": { + "hash": { + "description": "The specific commit hash that triggered this notice", + "type": "string" + }, + "pr": { + "description": "Pull request number (when appropriate)", + "type": "integer", + "format": "int32" + }, + "ref": { + "description": "The branch of commit hash", + "type": "string" + }, + "url": { + "description": "URL to the originator (eg, PR URL)", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RefItem": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "hash": { + "description": "The SHA hash of the commit this ref points to", + "type": "string" + }, + "name": { + "description": "The name of the reference (branch or tag)", + "type": "string", + "default": "" + }, + "refURL": { + "description": "The URL to the reference (branch or tag)", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RefList": { + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RefItem" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "RefList", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository": { + "description": "When this code is changed, make sure to update the code generation. As of writing, this can be done via the hack dir in the root of the repo: ./hack/update-codegen.sh provisioning If you've opened the generated files in this dir at some point in VSCode, you may also have to re-open them to clear errors.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + ] + }, + "spec": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RepositorySpec" + } + ] + }, + "status": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RepositoryStatus" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "Repository", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RepositoryList": { + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.Repository" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "RepositoryList", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RepositorySpec": { + "type": "object", + "required": [ + "title", + "workflows", + "sync", + "type" + ], + "properties": { + "bitbucket": { + "description": "The repository on Bitbucket. Mutually exclusive with local | github | git.", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.BitbucketRepositoryConfig" + } + ] + }, + "description": { + "description": "Repository description", + "type": "string" + }, + "git": { + "description": "The repository on Git. Mutually exclusive with local | github | git.", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.GitRepositoryConfig" + } + ] + }, + "github": { + "description": "The repository on GitHub. Mutually exclusive with local | github | git.", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.GitHubRepositoryConfig" + } + ] + }, + "gitlab": { + "description": "The repository on GitLab. Mutually exclusive with local | github | git.", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.GitLabRepositoryConfig" + } + ] + }, + "local": { + "description": "The repository on the local file system. Mutually exclusive with local | github.", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.LocalRepositoryConfig" + } + ] + }, + "sync": { + "description": "Sync settings -- how values are pulled from the repository into grafana", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.SyncOptions" + } + ] + }, + "title": { + "description": "The repository display name (shown in the UI)", + "type": "string", + "default": "" + }, + "type": { + "description": "The repository type. When selected oneOf the values below should be non-nil\n\nPossible enum values:\n - `\"bitbucket\"`\n - `\"git\"`\n - `\"github\"`\n - `\"gitlab\"`\n - `\"local\"`", + "type": "string", + "default": "", + "enum": [ + "bitbucket", + "git", + "github", + "gitlab", + "local" + ] + }, + "workflows": { + "description": "UI driven Workflow that allow changes to the contends of the repository. The order is relevant for defining the precedence of the workflows. When empty, the repository does not support any edits (eg, readonly)", + "type": "array", + "items": { + "type": "string", + "default": "", + "enum": [ + "branch", + "write" + ] + } + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.RepositoryStatus": { + "description": "The status of a Repository. This is expected never to be created by a kubectl call or similar, and is expected to rarely (if ever) be edited manually. As such, it is also a little less well structured than the spec, such as conditional-but-ever-present fields.", + "type": "object", + "required": [ + "observedGeneration", + "health", + "sync", + "webhook" + ], + "properties": { + "health": { + "description": "This will get updated with the current health status (and updated periodically)", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.HealthStatus" + } + ] + }, + "observedGeneration": { + "description": "The generation of the spec last time reconciliation ran", + "type": "integer", + "format": "int64", + "default": 0 + }, + "stats": { + "description": "The object count when sync last ran", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceCount" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "sync": { + "description": "Sync information with the last sync information", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.SyncStatus" + } + ] + }, + "webhook": { + "description": "Webhook Information (if applicable)", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.WebhookStatus" + } + ] + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceCount": { + "type": "object", + "required": [ + "group", + "resource", + "count" + ], + "properties": { + "count": { + "type": "integer", + "format": "int64", + "default": 0 + }, + "group": { + "type": "string", + "default": "" + }, + "resource": { + "type": "string", + "default": "" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceList": { + "description": "Information we can get just from the file listing", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceListItem" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "ResourceList", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceListItem": { + "type": "object", + "required": [ + "path", + "group", + "resource", + "name", + "hash" + ], + "properties": { + "folder": { + "type": "string" + }, + "group": { + "type": "string", + "default": "" + }, + "hash": { + "description": "the k8s identifier", + "type": "string", + "default": "" + }, + "name": { + "type": "string", + "default": "" + }, + "path": { + "type": "string", + "default": "" + }, + "resource": { + "type": "string", + "default": "" + }, + "time": { + "type": "integer", + "format": "int64" + }, + "title": { + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceObjects": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "action": { + "description": "The action required/used for dryRun\n\nPossible enum values:\n - `\"create\"`\n - `\"delete\"`\n - `\"move\"`\n - `\"update\"`", + "type": "string", + "enum": [ + "create", + "delete", + "move", + "update" + ] + }, + "dryRun": { + "description": "The value returned from a dryRun request", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" + } + ] + }, + "existing": { + "description": "The same value, currently saved in the grafana database", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" + } + ] + }, + "file": { + "description": "The resource from the repository with all modifications applied eg, the name, folder etc will all be applied to this object", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" + } + ] + }, + "type": { + "description": "The identified type for this object", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceType" + } + ] + }, + "upsert": { + "description": "For write events, this will return the value that was added or updated", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" + } + ] + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceRef": { + "type": "object", + "properties": { + "group": { + "description": "Group is the group of the resource, such as \"dashboard.grafana.app\".", + "type": "string" + }, + "kind": { + "description": "Kind is the type of resource, for example, \"Dashboard\".", + "type": "string" + }, + "name": { + "description": "Name is the name of the resource, such as a dashboard UID.", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceRepositoryInfo": { + "type": "object", + "required": [ + "type", + "title", + "namespace", + "name" + ], + "properties": { + "name": { + "description": "The name (identifier)", + "type": "string", + "default": "" + }, + "namespace": { + "description": "The namespace this belongs to", + "type": "string", + "default": "" + }, + "title": { + "description": "The display name for this repository", + "type": "string", + "default": "" + }, + "type": { + "description": "The repository type\n\nPossible enum values:\n - `\"bitbucket\"`\n - `\"git\"`\n - `\"github\"`\n - `\"gitlab\"`\n - `\"local\"`", + "type": "string", + "default": "", + "enum": [ + "bitbucket", + "git", + "github", + "gitlab", + "local" + ] + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceType": { + "type": "object", + "properties": { + "classic": { + "description": "For non-k8s native formats, what did this start as\n\nPossible enum values:\n - `\"access-control\"` Access control https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/access-control/sample.yaml\n - `\"alerting\"` Alert configuration https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/alerting/sample.yaml\n - `\"dashboard\"` Dashboard JSON\n - `\"datasources\"` Datasource definitions eg: https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/datasources/sample.yaml", + "type": "string", + "enum": [ + "access-control", + "alerting", + "dashboard", + "datasources" + ] + }, + "group": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceURLs": { + "type": "object", + "properties": { + "compareURL": { + "description": "Compare this version to the target branch", + "type": "string" + }, + "newPullRequestURL": { + "description": "A URL that will create a new pull requeset for this branch", + "type": "string" + }, + "repositoryURL": { + "description": "A URL pointing to the repository this lives in", + "type": "string" + }, + "sourceURL": { + "description": "A URL pointing to the this file in the repository", + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceWrapper": { + "description": "This is a container type for any resource type", + "type": "object", + "required": [ + "repository", + "resource" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "errors": { + "description": "If errors exist, show them here", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + }, + "hash": { + "description": "The repo hash value", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "path": { + "description": "Path to the remote file", + "type": "string" + }, + "ref": { + "description": "The request ref (or branch if exists)", + "type": "string" + }, + "repository": { + "description": "Basic repository info", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceRepositoryInfo" + } + ] + }, + "resource": { + "description": "Different flavors of the same object", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceObjects" + } + ] + }, + "timestamp": { + "description": "The modified time in the remote file system", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ] + }, + "urls": { + "description": "Typed links for this file (only supported by external systems, github etc)", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ResourceURLs" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "ResourceWrapper", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.SyncJobOptions": { + "type": "object", + "required": [ + "incremental" + ], + "properties": { + "incremental": { + "description": "Incremental synchronization for versioned repositories", + "type": "boolean", + "default": false + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.SyncOptions": { + "type": "object", + "required": [ + "enabled", + "target" + ], + "properties": { + "enabled": { + "description": "Enabled must be saved as true before any sync job will run", + "type": "boolean", + "default": false + }, + "intervalSeconds": { + "description": "When non-zero, the sync will run periodically", + "type": "integer", + "format": "int64" + }, + "target": { + "description": "Where values should be saved\n\nPossible enum values:\n - `\"folder\"` Resources will be saved into a folder managed by this repository It will contain a copy of everything from the remote The folder k8s name will be the same as the repository k8s name\n - `\"instance\"` Resources are saved in the global context Only one repository may specify the `instance` target When this exists, the UI will promote writing to the instance repo rather than the grafana database (where possible)", + "type": "string", + "default": "", + "enum": [ + "folder", + "instance" + ] + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.SyncStatus": { + "type": "object", + "required": [ + "state", + "message" + ], + "properties": { + "finished": { + "description": "When the sync job finished", + "type": "integer", + "format": "int64" + }, + "incremental": { + "description": "Incremental synchronization for versioned repositories", + "type": "boolean" + }, + "job": { + "description": "The ID for the job that ran this sync", + "type": "string" + }, + "lastRef": { + "description": "The repository ref when the last successful sync ran", + "type": "string" + }, + "message": { + "description": "Summary messages (will be shown to users)", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + }, + "scheduled": { + "description": "When the next sync check is scheduled", + "type": "integer", + "format": "int64" + }, + "started": { + "description": "When the sync job started", + "type": "integer", + "format": "int64" + }, + "state": { + "description": "pending, running, success, error\n\nPossible enum values:\n - `\"error\"` Finished with errors\n - `\"pending\"` Job has been submitted, but not processed yet\n - `\"success\"` Finished with success\n - `\"warning\"` Finished with some non-critical errors\n - `\"working\"` The job is running", + "type": "string", + "default": "", + "enum": [ + "error", + "pending", + "success", + "warning", + "working" + ] + } + } + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.TestResults": { + "description": "HistoryList is a list of versions of a resource", + "type": "object", + "required": [ + "code", + "success" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "code": { + "description": "HTTP status code", + "type": "integer", + "format": "int32", + "default": 0 + }, + "errors": { + "description": "Field related errors", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.ErrorDetails" + } + ] + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "success": { + "description": "Is the connection healthy", + "type": "boolean", + "default": false + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "TestResults", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.WebhookResponse": { + "type": "object", + "properties": { + "added": { + "description": "Optional message", + "type": "string" + }, + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "code": { + "description": "HTTP Status code 200 implies that the payload was understood but nothing is required 202 implies that an async job has been scheduled to handle the request", + "type": "integer", + "format": "int32" + }, + "job": { + "description": "Jobs to be processed When the response is 202 (Accepted) the queued jobs will be returned", + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.JobSpec" + } + ] + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "provisioning.grafana.app", + "kind": "WebhookResponse", + "version": "v0alpha1" + } + ] + }, + "com.github.grafana.grafana.apps.provisioning.pkg.apis.provisioning.v0alpha1.WebhookStatus": { + "type": "object", + "properties": { + "encryptedSecret": { + "type": "string", + "format": "byte" + }, + "id": { + "type": "integer", + "format": "int64" + }, + "lastEvent": { + "type": "integer", + "format": "int64" + }, + "secret": { + "type": "string" + }, + "subscribedEvents": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "url": { + "type": "string" + } + } + }, "com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured": { "type": "object", "additionalProperties": true, @@ -2650,12 +4076,7 @@ "description": "Resources to delete This option has been created because currently the frontend does not use standarized app platform APIs. For performance and API consistency reasons, the preferred option is it to use the paths.", "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceRef" - } - ] + "default": {} } } } @@ -2951,37 +4372,15 @@ "type": "string" }, "metadata": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" - } - ] + "default": {} }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobSpec" - } - ] + "default": {} }, "status": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobStatus" - } - ] + "default": {} } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "provisioning.grafana.app", - "kind": "Job", - "version": "v0alpha1" - } - ] + } }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobList": { "type": "object", @@ -2996,12 +4395,7 @@ "items": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Job" - } - ] + "default": {} } }, "kind": { @@ -3009,21 +4403,9 @@ "type": "string" }, "metadata": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - ] + "default": {} } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "provisioning.grafana.app", - "kind": "JobList", - "version": "v0alpha1" - } - ] + } }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobResourceSummary": { "type": "object", @@ -3090,52 +4472,22 @@ ] }, "delete": { - "description": "Delete when the action is `delete`", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.DeleteJobOptions" - } - ] + "description": "Delete when the action is `delete`" }, "migrate": { - "description": "Required when the action is `migrate`", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.MigrateJobOptions" - } - ] + "description": "Required when the action is `migrate`" }, "move": { - "description": "Move when the action is `move`", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.MoveJobOptions" - } - ] + "description": "Move when the action is `move`" }, "pr": { - "description": "Pull request options", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.PullRequestJobOptions" - } - ] + "description": "Pull request options" }, "pull": { - "description": "Required when the action is `pull`", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.SyncJobOptions" - } - ] + "description": "Required when the action is `pull`" }, "push": { - "description": "Required when the action is `push`", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ExportJobOptions" - } - ] + "description": "Required when the action is `push`" }, "repository": { "description": "The the repository reference (for now also in labels) This value is required, but will be popuplated from the job making the request", @@ -3184,9 +4536,7 @@ "summary": { "description": "Summary of processed actions", "type": "array", - "items": { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobResourceSummary" - } + "items": {} } } }, @@ -3257,12 +4607,7 @@ "description": "Resources to move This option has been created because currently the frontend does not use standarized app platform APIs. For performance and API consistency reasons, the preferred option is it to use the paths.", "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceRef" - } - ] + "default": {} } }, "targetPath": { @@ -3327,12 +4672,7 @@ "items": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RefItem" - } - ] + "default": {} }, "x-kubernetes-list-type": "atomic" }, @@ -3341,21 +4681,9 @@ "type": "string" }, "metadata": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - ] + "default": {} } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "provisioning.grafana.app", - "kind": "RefList", - "version": "v0alpha1" - } - ] + } }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository": { "description": "When this code is changed, make sure to update the code generation. As of writing, this can be done via the hack dir in the root of the repo: ./hack/update-codegen.sh provisioning If you've opened the generated files in this dir at some point in VSCode, you may also have to re-open them to clear errors.", @@ -3370,37 +4698,15 @@ "type": "string" }, "metadata": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" - } - ] + "default": {} }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositorySpec" - } - ] + "default": {} }, "status": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositoryStatus" - } - ] + "default": {} } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "provisioning.grafana.app", - "kind": "Repository", - "version": "v0alpha1" - } - ] + } }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositoryList": { "type": "object", @@ -3415,12 +4721,7 @@ "items": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository" - } - ] + "default": {} }, "x-kubernetes-list-type": "atomic" }, @@ -3429,21 +4730,9 @@ "type": "string" }, "metadata": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - ] + "default": {} } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "provisioning.grafana.app", - "kind": "RepositoryList", - "version": "v0alpha1" - } - ] + } }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.RepositorySpec": { "type": "object", @@ -3455,57 +4744,27 @@ ], "properties": { "bitbucket": { - "description": "The repository on Bitbucket. Mutually exclusive with local | github | git.", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.BitbucketRepositoryConfig" - } - ] + "description": "The repository on Bitbucket. Mutually exclusive with local | github | git." }, "description": { "description": "Repository description", "type": "string" }, "git": { - "description": "The repository on Git. Mutually exclusive with local | github | git.", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.GitRepositoryConfig" - } - ] + "description": "The repository on Git. Mutually exclusive with local | github | git." }, "github": { - "description": "The repository on GitHub. Mutually exclusive with local | github | git.", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.GitHubRepositoryConfig" - } - ] + "description": "The repository on GitHub. Mutually exclusive with local | github | git." }, "gitlab": { - "description": "The repository on GitLab. Mutually exclusive with local | github | git.", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.GitLabRepositoryConfig" - } - ] + "description": "The repository on GitLab. Mutually exclusive with local | github | git." }, "local": { - "description": "The repository on the local file system. Mutually exclusive with local | github.", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.LocalRepositoryConfig" - } - ] + "description": "The repository on the local file system. Mutually exclusive with local | github." }, "sync": { "description": "Sync settings -- how values are pulled from the repository into grafana", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.SyncOptions" - } - ] + "default": {} }, "title": { "description": "The repository display name (shown in the UI)", @@ -3550,12 +4809,7 @@ "properties": { "health": { "description": "This will get updated with the current health status (and updated periodically)", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.HealthStatus" - } - ] + "default": {} }, "observedGeneration": { "description": "The generation of the spec last time reconciliation ran", @@ -3567,31 +4821,16 @@ "description": "The object count when sync last ran", "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceCount" - } - ] + "default": {} }, "x-kubernetes-list-type": "atomic" }, "sync": { "description": "Sync information with the last sync information", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.SyncStatus" - } - ] + "default": {} }, "webhook": { - "description": "Webhook Information (if applicable)", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.WebhookStatus" - } - ] + "description": "Webhook Information (if applicable)" } } }, @@ -3738,12 +4977,7 @@ "items": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceListItem" - } - ] + "default": {} }, "x-kubernetes-list-type": "atomic" }, @@ -3752,21 +4986,9 @@ "type": "string" }, "metadata": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - ] + "default": {} } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "provisioning.grafana.app", - "kind": "ResourceList", - "version": "v0alpha1" - } - ] + } }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceListItem": { "type": "object", @@ -3828,45 +5050,20 @@ ] }, "dryRun": { - "description": "The value returned from a dryRun request", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" - } - ] + "description": "The value returned from a dryRun request" }, "existing": { - "description": "The same value, currently saved in the grafana database", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" - } - ] + "description": "The same value, currently saved in the grafana database" }, "file": { - "description": "The resource from the repository with all modifications applied eg, the name, folder etc will all be applied to this object", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" - } - ] + "description": "The resource from the repository with all modifications applied eg, the name, folder etc will all be applied to this object" }, "type": { "description": "The identified type for this object", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceType" - } - ] + "default": {} }, "upsert": { - "description": "For write events, this will return the value that was added or updated", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured" - } - ] + "description": "For write events, this will return the value that was added or updated" } } }, @@ -4053,46 +5250,19 @@ }, "repository": { "description": "Basic repository info", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceRepositoryInfo" - } - ] + "default": {} }, "resource": { "description": "Different flavors of the same object", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceObjects" - } - ] + "default": {} }, "timestamp": { - "description": "The modified time in the remote file system", - "allOf": [ - { - "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - ] + "description": "The modified time in the remote file system" }, "urls": { - "description": "Typed links for this file (only supported by external systems, github etc)", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ResourceURLs" - } - ] + "description": "Typed links for this file (only supported by external systems, github etc)" } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "provisioning.grafana.app", - "kind": "ResourceWrapper", - "version": "v0alpha1" - } - ] + } }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.SyncJobOptions": { "type": "object", @@ -4214,12 +5384,7 @@ "description": "Field related errors", "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.ErrorDetails" - } - ] + "default": {} } }, "kind": { @@ -4231,14 +5396,7 @@ "type": "boolean", "default": false } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "provisioning.grafana.app", - "kind": "TestResults", - "version": "v0alpha1" - } - ] + } }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.WebhookResponse": { "type": "object", @@ -4257,25 +5415,13 @@ "format": "int32" }, "job": { - "description": "Jobs to be processed When the response is 202 (Accepted) the queued jobs will be returned", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobSpec" - } - ] + "description": "Jobs to be processed When the response is 202 (Accepted) the queued jobs will be returned" }, "kind": { "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", "type": "string" } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "provisioning.grafana.app", - "kind": "WebhookResponse", - "version": "v0alpha1" - } - ] + } }, "com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.WebhookStatus": { "type": "object", diff --git a/pkg/tests/apis/provisioning/helper_test.go b/pkg/tests/apis/provisioning/helper_test.go index 2d5585b819f..f1252af3662 100644 --- a/pkg/tests/apis/provisioning/helper_test.go +++ b/pkg/tests/apis/provisioning/helper_test.go @@ -27,7 +27,7 @@ import ( dashboardsV2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1" dashboardsV2beta1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1" folder "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" "github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs" "github.com/grafana/grafana/pkg/services/featuremgmt" @@ -112,11 +112,11 @@ func (h *provisioningTestHelper) AwaitJobSuccess(t *testing.T, ctx context.Conte require.NoError(t, err) require.NotNil(t, result) + errors := mustNestedStringSlice(result.Object, "status", "errors") + require.Empty(t, errors, "historic job '%s' has errors: %v", job.GetName(), errors) state := mustNestedString(result.Object, "status", "state") require.Equal(t, string(provisioning.JobStateSuccess), state, "historic job '%s' was not successful", job.GetName()) - errors := mustNestedStringSlice(result.Object, "status", "errors") - require.Empty(t, errors, "historic job '%s' has errors: %v", job.GetName(), errors) }, time.Second*10, time.Millisecond*25) { // We also want to add the job details to the error when it fails. job, err := h.Jobs.Resource.Get(ctx, job.GetName(), metav1.GetOptions{}) @@ -163,6 +163,50 @@ func (h *provisioningTestHelper) AwaitJobs(t *testing.T, repoName string) { } } +// AwaitJobsWithStates waits for all jobs for a repository to complete and accepts multiple valid end states +func (h *provisioningTestHelper) AwaitJobsWithStates(t *testing.T, repoName string, acceptedStates []string) { + t.Helper() + + // First, we wait for all jobs for the repository to disappear (i.e. complete/fail). + require.EventuallyWithT(t, func(collect *assert.CollectT) { + list, err := h.Jobs.Resource.List(context.Background(), metav1.ListOptions{}) + if assert.NoError(collect, err, "failed to list active jobs") { + for _, elem := range list.Items { + repo, _, err := unstructured.NestedString(elem.Object, "spec", "repository") + require.NoError(t, err) + if repo == repoName { + collect.Errorf("there are still remaining jobs for %s: %+v", repoName, elem) + return + } + } + } + }, time.Second*10, time.Millisecond*25, "job queue must be empty") + + // Then, as all jobs are now historic jobs, we make sure they are in an accepted state. + result, err := h.Repositories.Resource.Get(context.Background(), repoName, metav1.GetOptions{}, "jobs") + require.NoError(t, err, "failed to list historic jobs") + + list, err := result.ToList() + require.NoError(t, err, "results should be a list") + require.NotEmpty(t, list.Items, "expect at least one job") + + for _, elem := range list.Items { + require.Equal(t, repoName, elem.GetLabels()[jobs.LabelRepository], "should have repo label") + + state := mustNestedString(elem.Object, "status", "state") + + // Check if state is in accepted states + found := false + for _, acceptedState := range acceptedStates { + if state == acceptedState { + found = true + break + } + } + require.True(t, found, "job %s completed with unexpected state %s (expected one of %v): %+v", elem.GetName(), state, acceptedStates, elem.Object) + } +} + // RenderObject reads the filePath and renders it as a template with the given values. // The template is expected to be a YAML or JSON file. // diff --git a/pkg/tests/apis/provisioning/provisioning_test.go b/pkg/tests/apis/provisioning/provisioning_test.go index cdf210e2133..b7b845e233f 100644 --- a/pkg/tests/apis/provisioning/provisioning_test.go +++ b/pkg/tests/apis/provisioning/provisioning_test.go @@ -20,8 +20,8 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/extensions" "github.com/grafana/grafana/pkg/infra/usagestats" "github.com/grafana/grafana/pkg/tests/apis" @@ -1378,27 +1378,29 @@ func TestIntegrationProvisioning_MoveJob(t *testing.T) { require.Error(t, err, "original file should be gone from repository") require.True(t, apierrors.IsNotFound(err), "should be not found error") - // Verify dashboard still exists in Grafana after sync - dashboards, err := helper.DashboardsV1.Resource.List(ctx, metav1.ListOptions{}) - require.NoError(t, err) - require.Len(t, dashboards.Items, 3, "should still have 3 dashboards after move") - - // Verify that dashboards have the correct source paths - foundPaths := make(map[string]bool) - for _, dashboard := range dashboards.Items { - sourcePath := dashboard.GetAnnotations()["grafana.app/sourcePath"] - foundPaths[sourcePath] = true - } - - require.True(t, foundPaths["moved/dashboard1.json"], "should have dashboard with moved source path") - require.True(t, foundPaths["dashboard2.json"], "should have dashboard2 in original location") - require.True(t, foundPaths["folder/dashboard3.json"], "should have dashboard3 in original nested location") - // Verify other files still exist at original locations _, err = helper.Repositories.Resource.Get(ctx, repo, metav1.GetOptions{}, "files", "dashboard2.json") require.NoError(t, err, "other files should still exist") _, err = helper.Repositories.Resource.Get(ctx, repo, metav1.GetOptions{}, "files", "folder", "dashboard3.json") require.NoError(t, err, "nested files should still exist") + + // Verify dashboard still exists in Grafana after sync + // Use eventually to let unified storage reflect the changes in dashboards. + require.EventuallyWithT(t, func(collect *assert.CollectT) { + dashboards, err := helper.DashboardsV1.Resource.List(ctx, metav1.ListOptions{}) + assert.NoError(collect, err) + assert.Len(collect, dashboards.Items, 3, "should still have 3 dashboards after move") + // Verify that dashboards have the correct source paths + foundPaths := make(map[string]bool) + for _, dashboard := range dashboards.Items { + sourcePath := dashboard.GetAnnotations()["grafana.app/sourcePath"] + foundPaths[sourcePath] = true + } + + assert.True(t, foundPaths["moved/dashboard1.json"], "should have dashboard with moved source path") + assert.True(t, foundPaths["dashboard2.json"], "should have dashboard2 in original location") + assert.True(t, foundPaths["folder/dashboard3.json"], "should have dashboard3 in original nested location") + }, time.Second*10, time.Millisecond*100, "Expected to eventually have 3 dashboards after move") }) t.Run("move multiple files and folder", func(t *testing.T) { @@ -2107,3 +2109,196 @@ func TestIntegrationProvisioning_MoveResources(t *testing.T) { }, time.Second*10, time.Millisecond*100, "Expected move job to handle non-existent resource") }) } + +func TestIntegrationProvisioning_SecondRepositoryOnlyExportsNewDashboards(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + helper := runGrafana(t) + ctx := context.Background() + + // Create some unmanaged dashboards directly in Grafana first + dashboard1 := helper.LoadYAMLOrJSONFile("exportunifiedtorepository/dashboard-test-v1.yaml") + dashboard1Obj, err := helper.DashboardsV1.Resource.Create(ctx, dashboard1, metav1.CreateOptions{}) + require.NoError(t, err, "should be able to create first dashboard") + dashboard1Name := dashboard1Obj.GetName() + + dashboard2 := helper.LoadYAMLOrJSONFile("exportunifiedtorepository/dashboard-test-v2beta1.yaml") + dashboard2Obj, err := helper.DashboardsV2beta1.Resource.Create(ctx, dashboard2, metav1.CreateOptions{}) + require.NoError(t, err, "should be able to create second dashboard") + dashboard2Name := dashboard2Obj.GetName() + + // Create the first repository with sync enabled + const repo1 = "first-repository" + repo1Path := filepath.Join(helper.ProvisioningPath, repo1) + err = os.MkdirAll(repo1Path, 0750) + require.NoError(t, err, "should be able to create repository path") + + createBody1 := helper.RenderObject(t, "testdata/local-write.json.tmpl", map[string]any{ + "Name": repo1, + "SyncEnabled": true, + "SyncTarget": "folder", + "Path": repo1Path, + }) + _, err = helper.Repositories.Resource.Create(ctx, createBody1, metav1.CreateOptions{}) + require.NoError(t, err, "should be able to create first repository") + + // Print file tree before export + printFileTree(t, helper.ProvisioningPath) + + // Initial export + result := helper.AdminREST.Post(). + Namespace("default"). + Resource("repositories"). + Name(repo1). + SubResource("jobs"). + SetHeader("Content-Type", "application/json"). + Body(asJSON(&provisioning.JobSpec{ + Push: &provisioning.ExportJobOptions{ + Folder: "", // export entire instance + Path: "", // no prefix necessary for testing + }, + })). + Do(ctx) + require.NoError(t, result.Error(), "should be able to create export job for first repo") + helper.AwaitJobsWithStates(t, repo1, []string{"success"}) + // Wait for first repository to sync + helper.SyncAndWait(t, repo1, nil) + + printFileTree(t, helper.ProvisioningPath) + // Verify that the first repository has claimed ownership of the dashboards + managedDash1, err := helper.DashboardsV1.Resource.Get(ctx, dashboard1Name, metav1.GetOptions{}) + require.NoError(t, err) + require.Equal(t, repo1, managedDash1.GetAnnotations()[utils.AnnoKeyManagerIdentity], "dashboard1 should be managed by first repo") + + managedDash2, err := helper.DashboardsV2beta1.Resource.Get(ctx, dashboard2Name, metav1.GetOptions{}) + require.NoError(t, err) + require.Equal(t, repo1, managedDash2.GetAnnotations()[utils.AnnoKeyManagerIdentity], "dashboard2 should be managed by first repo") + + // Create second repository - enable sync and set different target + + const repo2 = "second-repository" + repo2Path := filepath.Join(helper.ProvisioningPath, repo2) + err = os.MkdirAll(repo2Path, 0750) + require.NoError(t, err, "should be able to create seconrd repository path") + + printFileTree(t, helper.ProvisioningPath) + + createBody2 := helper.RenderObject(t, "testdata/local-write.json.tmpl", map[string]any{ + "Name": repo2, + "SyncEnabled": true, + "SyncTarget": "folder", + "Path": repo2Path, + }) + + _, err = helper.Repositories.Resource.Create(ctx, createBody2, metav1.CreateOptions{}) + require.NoError(t, err, "should be able to create second repository") + + // Wait for second repository to sync + helper.SyncAndWait(t, repo2, nil) + + // Validate that folders for both repositories exist + folders, err := helper.Folders.Resource.List(ctx, metav1.ListOptions{}) + require.NoError(t, err, "should be able to list folders") + + var repo1FolderFound, repo2FolderFound bool + for _, folder := range folders.Items { + if folder.GetName() == repo1 { + repo1FolderFound = true + } + if folder.GetName() == repo2 { + repo2FolderFound = true + } + } + require.True(t, repo1FolderFound, "folder for first repository %s should exist after sync", repo1) + require.True(t, repo2FolderFound, "folder for second repository %s should exist after sync", repo2) + + // Create a third dashboard that won't be claimed by the first repo + dashboard3 := helper.LoadYAMLOrJSONFile("exportunifiedtorepository/dashboard-test-v0.yaml") + dashboard3Obj, err := helper.DashboardsV0.Resource.Create(ctx, dashboard3, metav1.CreateOptions{}) + require.NoError(t, err, "should be able to create third dashboard") + dashboard3Name := dashboard3Obj.GetName() + + // Verify dashboard3 is not managed by anyone initially + unmanagedDash3, err := helper.DashboardsV0.Resource.Get(ctx, dashboard3Name, metav1.GetOptions{}) + require.NoError(t, err) + manager, found := unmanagedDash3.GetAnnotations()[utils.AnnoKeyManagerIdentity] + require.True(t, !found || manager == "", "dashboard3 should not be managed initially") + + printFileTree(t, helper.ProvisioningPath) + // Count files in first repo before second export + files1Before, err := countFilesInDir(repo1Path) + require.NoError(t, err) + + // Export from second repository - this should only export the unmanaged dashboard3 + result = helper.AdminREST.Post(). + Namespace("default"). + Resource("repositories"). + Name(repo2). + SubResource("jobs"). + SetHeader("Content-Type", "application/json"). + Body(asJSON(&provisioning.JobSpec{ + Push: &provisioning.ExportJobOptions{ + Folder: "", // export entire instance + Path: "", // no prefix necessary for testing + }, + })). + Do(ctx) + require.NoError(t, result.Error(), "should be able to create export job for second repo") + + // Wait for second repository export to complete + helper.AwaitJobsWithStates(t, repo2, []string{"success"}) + + // Wait for second repository to sync + helper.SyncAndWait(t, repo1, nil) + helper.SyncAndWait(t, repo2, nil) + + printFileTree(t, helper.ProvisioningPath) + files1After, err := countFilesInDir(repo1Path) + require.NoError(t, err) + + actualNewFiles := files1After - files1Before + require.Equal(t, 0, actualNewFiles, + "second repository should skip managed dashboards and had folder issues with unmanaged dashboard (expected %d new files, got %d)", + 0, actualNewFiles) + + // Verify files in the second repository + files2After, err := countFilesInDir(repo2Path) + require.NoError(t, err) + require.Equal(t, 1, files2After, + "second repository should only export the unmanaged dashboard (expected %d new files, got %d)", + 1, files2After) + + // Verify dashboard1 and dashboard2 are still managed by repo1 (unchanged) + stillManagedDash1, err := helper.DashboardsV1.Resource.Get(ctx, dashboard1Name, metav1.GetOptions{}) + require.NoError(t, err) + require.Equal(t, repo1, stillManagedDash1.GetAnnotations()[utils.AnnoKeyManagerIdentity], + "dashboard1 should still be managed by first repo") + + stillManagedDash2, err := helper.DashboardsV2beta1.Resource.Get(ctx, dashboard2Name, metav1.GetOptions{}) + require.NoError(t, err) + require.Equal(t, repo1, stillManagedDash2.GetAnnotations()[utils.AnnoKeyManagerIdentity], + "dashboard2 should still be managed by first repo") + + // Verify dashboard3 is now managed by repo2 + stillManagedDash3, err := helper.DashboardsV0.Resource.Get(ctx, dashboard3Name, metav1.GetOptions{}) + require.NoError(t, err) + require.Equal(t, repo2, stillManagedDash3.GetAnnotations()[utils.AnnoKeyManagerIdentity], + "dashboard3 should now be managed by second repo") +} + +// Helper function to count files in a directory recursively +func countFilesInDir(rootPath string) (int, error) { + count := 0 + err := filepath.WalkDir(rootPath, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if !d.IsDir() { + count++ + } + return nil + }) + return count, err +} diff --git a/pkg/tests/apis/provisioning/secrets_test.go b/pkg/tests/apis/provisioning/secrets_test.go index a85b8a56f62..331d4bc3067 100644 --- a/pkg/tests/apis/provisioning/secrets_test.go +++ b/pkg/tests/apis/provisioning/secrets_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1" + provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/pkg/registry/apis/provisioning/secrets" "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" "github.com/stretchr/testify/require" diff --git a/pkg/tsdb/cloudwatch/accounts_test.go b/pkg/tsdb/cloudwatch/accounts_test.go index 67ac7587279..f788ae84897 100644 --- a/pkg/tsdb/cloudwatch/accounts_test.go +++ b/pkg/tsdb/cloudwatch/accounts_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/grafana/grafana-aws-sdk/pkg/awsauth" + "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" @@ -23,6 +24,9 @@ func newTestDatasource(opts ...func(*DataSource)) *DataSource { AWSConfigProvider: awsauth.NewFakeConfigProvider(false), logger: log.NewNullLogger(), tagValueCache: cache.New(0, 0), + Settings: models.CloudWatchSettings{ + AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "us-east-1"}, + }, } ds.resourceHandler = httpadapter.New(ds.newResourceMux()) for _, opt := range opts { diff --git a/pkg/tsdb/cloudwatch/cloudwatch.go b/pkg/tsdb/cloudwatch/cloudwatch.go index 919b853887e..1015393477b 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch.go +++ b/pkg/tsdb/cloudwatch/cloudwatch.go @@ -59,7 +59,7 @@ type DataSource struct { } func (ds *DataSource) newAWSConfig(ctx context.Context, region string) (aws.Config, error) { - if region == defaultRegion { + if region == defaultRegion || region == "" { if len(ds.Settings.Region) == 0 { return aws.Config{}, models.ErrMissingRegion } diff --git a/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go b/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go index 2278261468a..784f19cc401 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go +++ b/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go @@ -269,6 +269,7 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { t.Run("Should error for any request when a default region is not selected", func(t *testing.T) { ds := newTestDatasource(func(ds *DataSource) { ds.Settings.GrafanaSettings.ListMetricsPageLimit = 1000 + ds.Settings.Region = "" }) req := &backend.CallResourceRequest{ diff --git a/pkg/tsdb/elasticsearch/healthcheck.go b/pkg/tsdb/elasticsearch/healthcheck.go index a3e04aafb6f..928945691de 100644 --- a/pkg/tsdb/elasticsearch/healthcheck.go +++ b/pkg/tsdb/elasticsearch/healthcheck.go @@ -191,11 +191,15 @@ func validateIndex(ctx context.Context, ds *es.DatasourceInfo) (message string, return "Failed to unmarshal field capabilities response", "error" } if fieldCaps["error"] != nil { - if errorMessage, ok := fieldCaps["error"].(map[string]any)["reason"].(string); ok { - return fmt.Sprintf("Error validating index: %s", errorMessage), "warning" - } else { + errorMap, ok := fieldCaps["error"].(map[string]any) + if !ok { return "Error validating index", "warning" } + errorMessage, ok := errorMap["reason"].(string) + if !ok { + return "Error validating index", "warning" + } + return fmt.Sprintf("Error validating index: %s", errorMessage), "warning" } fields, ok := fieldCaps["fields"].(map[string]any) diff --git a/pkg/tsdb/elasticsearch/healthcheck_test.go b/pkg/tsdb/elasticsearch/healthcheck_test.go index b3f6dc97c93..48fd00e8adc 100644 --- a/pkg/tsdb/elasticsearch/healthcheck_test.go +++ b/pkg/tsdb/elasticsearch/healthcheck_test.go @@ -58,6 +58,16 @@ func Test_validateIndex_Warning_ErrorValidatingIndex(t *testing.T) { assert.Equal(t, "Elasticsearch data source is healthy. Warning: Error validating index: index_not_found", res.Message) } +func Test_validateIndex_Warning_ErrorValidatingIndex2(t *testing.T) { + service := GetMockService(http.StatusOK, "200 OK", `{"status":"green"}`, `{"error":"not a map"}`) + res, _ := service.CheckHealth(mockedCfg, &backend.CheckHealthRequest{ + PluginContext: backend.PluginContext{}, + Headers: nil, + }) + assert.Equal(t, backend.HealthStatusOk, res.Status) + assert.Equal(t, "Elasticsearch data source is healthy. Warning: Error validating index", res.Message) +} + func Test_validateIndex_Warning_WrongTimestampType(t *testing.T) { service := GetMockService(http.StatusOK, "200 OK", `{"status":"green"}`, `{"fields":{"timestamp":{"float":{"metadata_field":true}}}}`) res, _ := service.CheckHealth(mockedCfg, &backend.CheckHealthRequest{ diff --git a/pkg/tsdb/jaeger/client.go b/pkg/tsdb/jaeger/client.go index 653c0463892..d858aa48f56 100644 --- a/pkg/tsdb/jaeger/client.go +++ b/pkg/tsdb/jaeger/client.go @@ -115,11 +115,15 @@ func (j *JaegerClient) Operations(s string) ([]string, error) { } func (j *JaegerClient) Search(query *JaegerQuery, start, end int64) ([]TraceResponse, error) { - jaegerURL, err := url.Parse(j.url) + u, err := url.JoinPath(j.url, "/api/traces") if err != nil { - return []TraceResponse{}, fmt.Errorf("failed to parse Jaeger URL: %w", err) + return []TraceResponse{}, backend.DownstreamError(fmt.Errorf("failed to join url path: %w", err)) + } + + jaegerURL, err := url.Parse(u) + if err != nil { + return []TraceResponse{}, backend.DownstreamError(fmt.Errorf("failed to parse Jaeger URL: %w", err)) } - jaegerURL.Path = "/api/traces" var queryTags string if query.Tags != "" { @@ -135,7 +139,7 @@ func (j *JaegerClient) Search(query *JaegerQuery, start, end int64) ([]TraceResp marshaledTags, err := json.Marshal(tagMap) if err != nil { - return []TraceResponse{}, fmt.Errorf("failed to convert tags to JSON: %w", err) + return []TraceResponse{}, backend.DownstreamError(fmt.Errorf("failed to convert tags to JSON: %w", err)) } queryTags = string(marshaledTags) diff --git a/pkg/tsdb/jaeger/client_test.go b/pkg/tsdb/jaeger/client_test.go index afabdc4bccc..eceb103ab82 100644 --- a/pkg/tsdb/jaeger/client_test.go +++ b/pkg/tsdb/jaeger/client_test.go @@ -186,6 +186,19 @@ func TestJaegerClient_Search(t *testing.T) { expectError bool expectedError error }{ + { + name: "Preserves base path in Jaeger URL", + query: &JaegerQuery{ + Service: "test-service", + }, + start: 1735689600000000, + end: 1738368000000000, + mockResponse: `{"data":[{"traceID":"test-trace-id"}]}`, + mockStatusCode: http.StatusOK, + expectedURL: "/abc/api/traces?end=1738368000000000&service=test-service&start=1735689600000000", + expectError: false, + expectedError: nil, + }, { name: "Successful search with all parameters", query: &JaegerQuery{ @@ -245,6 +258,11 @@ func TestJaegerClient_Search(t *testing.T) { settings := backend.DataSourceInstanceSettings{ URL: server.URL, } + + if tt.name == "Preserves base path in Jaeger URL" { + settings.URL = server.URL + "/abc" + } + client, err := New(server.Client(), log.NewNullLogger(), settings) assert.NoError(t, err) traces, err := client.Search(tt.query, tt.start, tt.end) diff --git a/pkg/tsdb/tempo/metrics_stream.go b/pkg/tsdb/tempo/metrics_stream.go index 1c4bba6f713..85fac2df83b 100644 --- a/pkg/tsdb/tempo/metrics_stream.go +++ b/pkg/tsdb/tempo/metrics_stream.go @@ -20,6 +20,10 @@ import ( const MetricsPathPrefix = "metrics/" +type PartialTempoQuery struct { + MetricsQueryType *dataquery.MetricsQueryType +} + func (s *Service) runMetricsStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender, datasource *Datasource) error { ctx, span := tracing.DefaultTracer().Start(ctx, "datasource.tempo.runMetricsStream") defer span.End() @@ -35,6 +39,15 @@ func (s *Service) runMetricsStream(ctx context.Context, req *backend.RunStreamRe return err } + tempoQuery := &PartialTempoQuery{} + err = json.Unmarshal(req.Data, tempoQuery) + if err != nil { + response.Error = fmt.Errorf("error unmarshaling Tempo query model: %v", err) + span.RecordError(response.Error) + span.SetStatus(codes.Error, response.Error.Error()) + return err + } + var qrr *tempopb.QueryRangeRequest err = json.Unmarshal(req.Data, &qrr) if err != nil { @@ -56,6 +69,24 @@ func (s *Service) runMetricsStream(ctx context.Context, req *backend.RunStreamRe // Ideally this would be pushed higher, so it's set once for all rpc calls, but we have only one now. ctx = metadata.AppendToOutgoingContext(ctx, "User-Agent", backend.UserAgentFromContext(ctx).String()) + if isInstantQuery(tempoQuery.MetricsQueryType) { + instantQuery := &tempopb.QueryInstantRequest{ + Query: qrr.Query, + Start: qrr.Start, + End: qrr.End, + } + + stream, err := datasource.StreamingClient.MetricsQueryInstant(ctx, instantQuery) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + s.logger.Error("Error Search()", "err", err) + return err + } + + return s.processInstantMetricsStream(ctx, stream, sender) + } + stream, err := datasource.StreamingClient.MetricsQueryRange(ctx, qrr) if err != nil { span.RecordError(err) @@ -101,3 +132,38 @@ func (s *Service) processMetricsStream(ctx context.Context, query string, stream return nil } + +func (s *Service) processInstantMetricsStream(ctx context.Context, stream tempopb.StreamingQuerier_MetricsQueryInstantClient, sender StreamSender) error { + ctx, span := tracing.DefaultTracer().Start(ctx, "datasource.tempo.processStream") + defer span.End() + messageCount := 0 + for { + msg, err := stream.Recv() + messageCount++ + span.SetAttributes(attribute.Int("message_count", messageCount)) + if errors.Is(err, io.EOF) { + if err := s.sendResponse(ctx, nil, nil, dataquery.SearchStreamingStateDone, sender); err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + break + } + if err != nil { + s.logger.Error("Error receiving message", "err", err) + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + transformed := traceql.TransformInstantMetricsResponse(*msg) + + if err := s.sendResponse(ctx, transformed, msg.Metrics, dataquery.SearchStreamingStateStreaming, sender); err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + } + + return nil +} diff --git a/pkg/tsdb/tempo/traceql/metrics.go b/pkg/tsdb/tempo/traceql/metrics.go index ec057c80cca..f2f2fe778c9 100644 --- a/pkg/tsdb/tempo/traceql/metrics.go +++ b/pkg/tsdb/tempo/traceql/metrics.go @@ -9,7 +9,6 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana/pkg/tsdb/tempo/kinds/dataquery" "github.com/grafana/tempo/pkg/tempopb" v1 "github.com/grafana/tempo/pkg/tempopb/common/v1" ) @@ -61,7 +60,7 @@ func TransformMetricsResponse(query string, resp tempopb.QueryRangeResponse) []* return append(frames, exemplarFrames...) } -func TransformInstantMetricsResponse(query *dataquery.TempoQuery, resp tempopb.QueryInstantResponse) []*data.Frame { +func TransformInstantMetricsResponse(resp tempopb.QueryInstantResponse) []*data.Frame { frames := make([]*data.Frame, len(resp.Series)) for i, series := range resp.Series { diff --git a/pkg/tsdb/tempo/traceql/metrics_test.go b/pkg/tsdb/tempo/traceql/metrics_test.go index 0cf956f88a2..e7c9e06e404 100644 --- a/pkg/tsdb/tempo/traceql/metrics_test.go +++ b/pkg/tsdb/tempo/traceql/metrics_test.go @@ -5,7 +5,6 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/grafana/grafana/pkg/tsdb/tempo/kinds/dataquery" "github.com/grafana/tempo/pkg/tempopb" v1 "github.com/grafana/tempo/pkg/tempopb/common/v1" "github.com/stretchr/testify/assert" @@ -113,7 +112,6 @@ func TestTransformMetricsResponse_MultipleSeries(t *testing.T) { } func TestTransformInstantMetricsResponse(t *testing.T) { - query := &dataquery.TempoQuery{} resp := tempopb.QueryInstantResponse{ Series: []*tempopb.InstantSeries{ { @@ -123,7 +121,7 @@ func TestTransformInstantMetricsResponse(t *testing.T) { }, } - frames := TransformInstantMetricsResponse(query, resp) + frames := TransformInstantMetricsResponse(resp) assert.Len(t, frames, 1) frame := frames[0] diff --git a/pkg/tsdb/tempo/traceql_query.go b/pkg/tsdb/tempo/traceql_query.go index 43eea31f04c..1b5d31edad5 100644 --- a/pkg/tsdb/tempo/traceql_query.go +++ b/pkg/tsdb/tempo/traceql_query.go @@ -97,7 +97,7 @@ func (s *Service) runTraceQlQueryMetrics(ctx context.Context, pCtx backend.Plugi return res, err } - frames := traceql.TransformInstantMetricsResponse(tempoQuery, queryResponse) + frames := traceql.TransformInstantMetricsResponse(queryResponse) result.Frames = frames } else { var queryResponse tempopb.QueryRangeResponse diff --git a/public/app/api/clients/dashboard/v0alpha1/baseAPI.ts b/public/app/api/clients/dashboard/v0alpha1/baseAPI.ts new file mode 100644 index 00000000000..dca53563fa0 --- /dev/null +++ b/public/app/api/clients/dashboard/v0alpha1/baseAPI.ts @@ -0,0 +1,14 @@ +import { createApi } from '@reduxjs/toolkit/query/react'; + +import { createBaseQuery } from 'app/api/createBaseQuery'; +import { getAPIBaseURL } from 'app/api/utils'; + +export const BASE_URL = getAPIBaseURL('dashboard.grafana.app', 'v0alpha1'); + +export const api = createApi({ + reducerPath: 'dashboardAPIv0alpha1', + baseQuery: createBaseQuery({ + baseURL: BASE_URL, + }), + endpoints: () => ({}), +}); diff --git a/public/app/api/clients/dashboard/v0alpha1/endpoints.gen.ts b/public/app/api/clients/dashboard/v0alpha1/endpoints.gen.ts new file mode 100644 index 00000000000..8730157dedf --- /dev/null +++ b/public/app/api/clients/dashboard/v0alpha1/endpoints.gen.ts @@ -0,0 +1,53 @@ +import { api } from './baseAPI'; +export const addTagTypes = ['Search'] as const; +const injectedRtkApi = api + .enhanceEndpoints({ + addTagTypes, + }) + .injectEndpoints({ + endpoints: (build) => ({ + getSearch: build.query({ + query: (queryArg) => ({ + url: `/search`, + params: { + query: queryArg.query, + folder: queryArg.folder, + sort: queryArg.sort, + }, + }), + providesTags: ['Search'], + }), + }), + overrideExisting: false, + }); +export { injectedRtkApi as generatedAPI }; +export type GetSearchApiResponse = /** status 200 undefined */ { + /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */ + apiVersion?: string; + /** Facet results */ + facets?: { + [key: string]: any; + }; + /** The dashboard body (unstructured for now) */ + hits: any[]; + /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */ + kind?: string; + /** Max score */ + maxScore?: number; + /** Where the query started from */ + offset?: number; + /** Cost of running the query */ + queryCost?: number; + /** How are the results sorted */ + sortBy?: any; + /** The number of matching results */ + totalHits: number; +}; +export type GetSearchApiArg = { + /** user query string */ + query?: string; + /** search/list within a folder (not recursive) */ + folder?: string; + /** sortable field */ + sort?: string; +}; diff --git a/public/app/api/clients/dashboard/v0alpha1/index.ts b/public/app/api/clients/dashboard/v0alpha1/index.ts new file mode 100644 index 00000000000..f61b46bc1f9 --- /dev/null +++ b/public/app/api/clients/dashboard/v0alpha1/index.ts @@ -0,0 +1,27 @@ +import { generatedAPI, GetSearchApiArg } from './endpoints.gen'; + +type OverrideGetSearchRequestOptions = GetSearchApiArg & { + type: string; +}; + +export const dashboardAPIv0alpha1 = generatedAPI.enhanceEndpoints({ + addTagTypes: ['Folder', 'Dashboard'], + endpoints: { + getSearch: (endpointDefinition) => { + const originalQuery = endpointDefinition.query; + endpointDefinition.providesTags = ['Search', 'Folder', 'Dashboard']; + if (originalQuery) { + // TODO: Remove once API spec is updated with `type` + endpointDefinition.query = (requestOptions: OverrideGetSearchRequestOptions) => ({ + ...originalQuery(requestOptions), + params: { + ...requestOptions, + type: requestOptions.type, + }, + }); + } + }, + }, +}); + +export const { useGetSearchQuery } = dashboardAPIv0alpha1; diff --git a/public/app/core/components/LocalStorageValueProvider/LocalStorageValueProvider.tsx b/public/app/core/components/LocalStorageValueProvider/LocalStorageValueProvider.tsx deleted file mode 100644 index 6d6804d5bba..00000000000 --- a/public/app/core/components/LocalStorageValueProvider/LocalStorageValueProvider.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { useEffect, useState } from 'react'; -import * as React from 'react'; - -import store from '../../store'; - -export interface Props { - storageKey: string; - defaultValue: T; - children: (value: T, onSaveToStore: (value: T) => void, onDeleteFromStore: () => void) => React.ReactNode; -} - -export const LocalStorageValueProvider = (props: Props) => { - const { children, storageKey, defaultValue } = props; - - const [state, setState] = useState({ value: store.getObject(props.storageKey, props.defaultValue) }); - - useEffect(() => { - const onStorageUpdate = (v: StorageEvent) => { - if (v.key === storageKey) { - setState({ value: store.getObject(props.storageKey, props.defaultValue) }); - } - }; - - window.addEventListener('storage', onStorageUpdate); - - return () => { - window.removeEventListener('storage', onStorageUpdate); - }; - }); - - const onSaveToStore = (value: T) => { - try { - store.setObject(storageKey, value); - } catch (error) { - console.error(error); - } - setState({ value }); - }; - - const onDeleteFromStore = () => { - try { - store.delete(storageKey); - } catch (error) { - console.log(error); - } - setState({ value: defaultValue }); - }; - - return <>{children(state.value, onSaveToStore, onDeleteFromStore)}; -}; diff --git a/public/app/core/components/LocalStorageValueProvider/index.tsx b/public/app/core/components/LocalStorageValueProvider/index.tsx deleted file mode 100644 index e4bde7ccaea..00000000000 --- a/public/app/core/components/LocalStorageValueProvider/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { LocalStorageValueProvider } from './LocalStorageValueProvider'; diff --git a/public/app/core/components/NestedFolderPicker/NestedFolderPicker.test.tsx b/public/app/core/components/NestedFolderPicker/NestedFolderPicker.test.tsx index 226583c54e1..38674034a0d 100644 --- a/public/app/core/components/NestedFolderPicker/NestedFolderPicker.test.tsx +++ b/public/app/core/components/NestedFolderPicker/NestedFolderPicker.test.tsx @@ -1,89 +1,31 @@ -import { fireEvent, render as rtlRender, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { HttpResponse, http } from 'msw'; -import { SetupServer, setupServer } from 'msw/node'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { fireEvent, render, screen } from 'test/test-utils'; -import { config } from '@grafana/runtime'; +import { config, setBackendSrv } from '@grafana/runtime'; +import { setupMockServer } from '@grafana/test-utils/server'; +import { getFolderFixtures } from '@grafana/test-utils/unstable'; import { backendSrv } from 'app/core/services/backend_srv'; -import { - treeViewersCanEdit, - wellFormedTree, -} from '../../../features/browse-dashboards/fixtures/dashboardsTreeItem.fixture'; - import { NestedFolderPicker } from './NestedFolderPicker'; -const [mockTree, { folderA, folderB, folderC, folderA_folderA, folderA_folderB }] = wellFormedTree(); -const [mockTreeThatViewersCanEdit /* shares folders with wellFormedTree */] = treeViewersCanEdit(); +const [_, { folderA, folderB, folderC, folderA_folderA, folderA_folderB, folderA_folderC }] = getFolderFixtures(); -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - getBackendSrv: () => backendSrv, -})); - -function render(...[ui, options]: Parameters) { - rtlRender({ui}, options); -} +setupMockServer(); +setBackendSrv(backendSrv); describe('NestedFolderPicker', () => { const mockOnChange = jest.fn(); const originalScrollIntoView = window.HTMLElement.prototype.scrollIntoView; - let server: SetupServer; beforeAll(() => { window.HTMLElement.prototype.scrollIntoView = function () {}; - - server = setupServer( - http.get('/api/folders/:uid', () => { - return HttpResponse.json({ - title: folderA.item.title, - uid: folderA.item.uid, - }); - }), - - http.get('/apis/provisioning.grafana.app/v0alpha1/namespaces/default/settings', () => { - return HttpResponse.json({ - items: [], - }); - }), - - http.get('/api/folders', ({ request }) => { - const url = new URL(request.url); - const parentUid = url.searchParams.get('parentUid') ?? undefined; - const permission = url.searchParams.get('permission'); - - const limit = parseInt(url.searchParams.get('limit') ?? '1000', 10); - const page = parseInt(url.searchParams.get('page') ?? '1', 10); - - const tree = permission === 'Edit' ? mockTreeThatViewersCanEdit : mockTree; - - // reconstruct a folder API response from the flat tree fixture - const folders = tree - .filter((v) => v.item.kind === 'folder' && v.item.parentUID === parentUid) - .map((folder) => { - return { - uid: folder.item.uid, - title: folder.item.kind === 'folder' ? folder.item.title : "invalid - this shouldn't happen", - }; - }) - .slice(limit * (page - 1), limit * page); - - return HttpResponse.json(folders); - }) - ); - - server.listen(); }); afterAll(() => { - server.close(); window.HTMLElement.prototype.scrollIntoView = originalScrollIntoView; }); afterEach(() => { jest.resetAllMocks(); - server.resetHandlers(); }); it('renders a button with the correct label when no folder is selected', async () => { @@ -92,18 +34,18 @@ describe('NestedFolderPicker', () => { }); it('renders a button with the correct label when a folder is selected', async () => { - render(); + render(); expect( await screen.findByRole('button', { name: `Select folder: ${folderA.item.title} currently selected` }) ).toBeInTheDocument(); }); it('clicking the button opens the folder picker', async () => { - render(); + const { user } = render(); // Open the picker and wait for children to load const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); await screen.findByLabelText(folderA.item.title); // Select folder button is no longer visible @@ -118,73 +60,73 @@ describe('NestedFolderPicker', () => { }); it('can select a folder from the picker', async () => { - render(); + const { user } = render(); // Open the picker and wait for children to load const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); await screen.findByLabelText(folderA.item.title); - await userEvent.click(screen.getByLabelText(folderA.item.title)); + await user.click(screen.getByLabelText(folderA.item.title)); expect(mockOnChange).toHaveBeenCalledWith(folderA.item.uid, folderA.item.title); }); it('can clear a selection if clearable is specified', async () => { - render(); + const { user } = render(); - await userEvent.click(await screen.findByRole('button', { name: 'Clear selection' })); + await user.click(await screen.findByRole('button', { name: 'Clear selection' })); expect(mockOnChange).toHaveBeenCalledWith(undefined, undefined); }); it('can select a folder from the picker with the keyboard', async () => { - render(); + const { user } = render(); const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); - await userEvent.keyboard('{ArrowDown}{ArrowDown}{Enter}'); - expect(mockOnChange).toHaveBeenCalledWith(folderA.item.uid, folderA.item.title); + await user.keyboard('{ArrowDown}{ArrowDown}{Enter}'); + expect(mockOnChange).toHaveBeenCalledWith(folderC.item.uid, folderC.item.title); }); it('shows the root folder by default', async () => { - render(); + const { user } = render(); // Open the picker and wait for children to load const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); await screen.findByLabelText(folderA.item.title); - await userEvent.click(screen.getByLabelText('Dashboards')); + await user.click(screen.getByLabelText('Dashboards')); expect(mockOnChange).toHaveBeenCalledWith('', 'Dashboards'); }); it('hides the root folder if the prop says so', async () => { - render(); + const { user } = render(); // Open the picker and wait for children to load const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); await screen.findByLabelText(folderA.item.title); expect(screen.queryByLabelText('Dashboards')).not.toBeInTheDocument(); }); it('hides folders specififed by UID', async () => { - render(); + const { user } = render(); // Open the picker and wait for children to load const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); await screen.findByLabelText(folderA.item.title); expect(screen.queryByLabelText(folderC.item.title)).not.toBeInTheDocument(); }); it('by default only shows items the user can edit', async () => { - render(); + const { user } = render(); const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); await screen.findByLabelText(folderA.item.title); expect(screen.queryByLabelText(folderB.item.title)).not.toBeInTheDocument(); // folderB is not editable @@ -192,10 +134,10 @@ describe('NestedFolderPicker', () => { }); it('shows items the user can view, with the prop', async () => { - render(); + const { user } = render(); const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); await screen.findByLabelText(folderA.item.title); expect(screen.getByLabelText(folderB.item.title)).toBeInTheDocument(); @@ -214,11 +156,11 @@ describe('NestedFolderPicker', () => { }); it('can expand and collapse a folder to show its children', async () => { - render(); + const { user } = render(); // Open the picker and wait for children to load const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); await screen.findByLabelText(folderA.item.title); // Expand Folder A @@ -240,34 +182,35 @@ describe('NestedFolderPicker', () => { fireEvent.mouseDown(screen.getByRole('button', { name: `Expand folder ${folderA.item.title}` })); // Select the first child - await userEvent.click(screen.getByLabelText(folderA_folderA.item.title)); + await user.click(screen.getByLabelText(folderA_folderA.item.title)); expect(mockOnChange).toHaveBeenCalledWith(folderA_folderA.item.uid, folderA_folderA.item.title); }); it('can expand and collapse a folder to show its children with the keyboard', async () => { - render(); + const { user } = render(); const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); // Expand Folder A - await userEvent.keyboard('{ArrowDown}{ArrowDown}{ArrowRight}'); + await user.keyboard('{ArrowDown}{ArrowDown}{ArrowDown}{ArrowDown}{ArrowRight}'); // Folder A's children are visible expect(await screen.findByLabelText(folderA_folderA.item.title)).toBeInTheDocument(); expect(await screen.findByLabelText(folderA_folderB.item.title)).toBeInTheDocument(); + expect(await screen.findByLabelText(folderA_folderC.item.title)).toBeInTheDocument(); // Collapse Folder A - await userEvent.keyboard('{ArrowLeft}'); + await user.keyboard('{ArrowLeft}'); expect(screen.queryByLabelText(folderA_folderA.item.title)).not.toBeInTheDocument(); expect(screen.queryByLabelText(folderA_folderB.item.title)).not.toBeInTheDocument(); // Expand Folder A again - await userEvent.keyboard('{ArrowRight}'); + await user.keyboard('{ArrowRight}'); // Select the first child - await userEvent.keyboard('{ArrowDown}{Enter}'); - expect(mockOnChange).toHaveBeenCalledWith(folderA_folderA.item.uid, folderA_folderA.item.title); + await user.keyboard('{ArrowDown}{Enter}'); + expect(mockOnChange).toHaveBeenCalledWith(folderA_folderC.item.uid, folderA_folderC.item.title); }); }); @@ -283,11 +226,11 @@ describe('NestedFolderPicker', () => { }); it('does not show an expand button', async () => { - render(); + const { user } = render(); // Open the picker and wait for children to load const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); await screen.findByLabelText(folderA.item.title); // There should be no expand button @@ -296,13 +239,13 @@ describe('NestedFolderPicker', () => { }); it('does not expand a folder with the keyboard', async () => { - render(); + const { user } = render(); const button = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(button); + await user.click(button); // try to expand Folder A - await userEvent.keyboard('{ArrowDown}{ArrowDown}{ArrowRight}'); + await user.keyboard('{ArrowDown}{ArrowDown}{ArrowRight}'); // Folder A's children are not visible expect(screen.queryByLabelText(folderA_folderA.item.title)).not.toBeInTheDocument(); diff --git a/public/app/core/components/NestedFolderPicker/useFoldersQuery.test.tsx b/public/app/core/components/NestedFolderPicker/useFoldersQuery.test.tsx index 18d83068b5e..cf0f0f67af1 100644 --- a/public/app/core/components/NestedFolderPicker/useFoldersQuery.test.tsx +++ b/public/app/core/components/NestedFolderPicker/useFoldersQuery.test.tsx @@ -1,82 +1,26 @@ -import { act, renderHook } from '@testing-library/react'; +import { ReactNode } from 'react'; +import { act, getWrapper, renderHook, waitFor } from 'test/test-utils'; import { GrafanaConfig } from '@grafana/data'; import * as runtime from '@grafana/runtime'; -import { DashboardsTreeItem } from 'app/features/browse-dashboards/types'; +import { setupMockServer } from '@grafana/test-utils/server'; +import { getFolderFixtures } from '@grafana/test-utils/unstable'; +import { backendSrv } from 'app/core/services/backend_srv'; import { DashboardViewItem } from '../../../features/search/types'; import { useFoldersQuery } from './useFoldersQuery'; import { getRootFolderItem } from './utils'; -const PAGE_SIZE = 10; +const [_, { folderA, folderB, folderC }] = getFolderFixtures(); -const legacyResponse = { - status: 'fulfilled', - originalArgs: { parentUid: undefined, page: 1, limit: PAGE_SIZE, permission: 'Edit' }, - data: [{ title: 'Legacy Folder', uid: 'legacy1', managedBy: undefined }], +runtime.setBackendSrv(backendSrv); +setupMockServer(); + +const wrapper = ({ children }: { children: ReactNode }) => { + const ProviderWrapper = getWrapper({ renderWithRouter: true }); + return {children}; }; -// Mock the legacy API client -jest.mock('app/features/browse-dashboards/api/browseDashboardsAPI', () => { - const PAGE_SIZE = 10; - return { - PAGE_SIZE, - browseDashboardsAPI: { - endpoints: { - listFolders: { - select: jest.fn(() => () => legacyResponse), - initiate: jest.fn(() => ({ - arg: { parentUid: undefined, page: 1, limit: PAGE_SIZE, permission: 'Edit' }, - unsubscribe: jest.fn(), - })), - }, - }, - }, - }; -}); - -const appPlatfromResponse = { - status: 'fulfilled', - originalArgs: { name: 'general' }, - data: { - items: [ - { - metadata: { name: 'app1', annotations: {} }, - spec: { title: 'AppPlatform Folder' }, - }, - ], - }, -}; - -// Mock the appPlatform API client -jest.mock('app/api/clients/folder/v1beta1', () => ({ - folderAPIv1beta1: { - endpoints: { - getFolderChildren: { - select: jest.fn(() => () => appPlatfromResponse), - initiate: jest.fn((arg: unknown) => ({ - arg, - unsubscribe: jest.fn(), - })), - }, - }, - }, -})); - -// Mock getPaginationPlaceholders to return empty array for simplicity -jest.mock('app/features/browse-dashboards/state/utils', () => ({ - getPaginationPlaceholders: jest.fn((): DashboardsTreeItem[] => []), -})); - -// Mock useDispatch and useSelector to just pass through -jest.mock('app/types/store', () => { - const mod = jest.requireActual('app/types/store'); - return { - ...mod, - useDispatch: () => (val: unknown) => val, - useSelector: (selector: Function) => selector(), - }; -}); describe('useFoldersQuery', () => { let configBackup: GrafanaConfig; @@ -89,28 +33,40 @@ describe('useFoldersQuery', () => { runtime.config.featureToggles = configBackup.featureToggles; }); - it('returns data using legacy api', () => { - runtime.config.featureToggles.foldersAppPlatformAPI = false; - const items = testFn(); - expect((items[1].item as DashboardViewItem).title).toBe('Legacy Folder'); - }); + describe.each([ + // foldersAppPlatformAPI enabled + true, + // foldersAppPlatformAPI disabled + false, + ])('foldersAppPlatformAPI feature toggle set to %s', (featureToggleState) => { + it('returns data using legacy api', async () => { + runtime.config.featureToggles.foldersAppPlatformAPI = featureToggleState; + const [_dashboardsContainer, ...items] = await testFn(); - it('returns appPlatform hook result when foldersAppPlatformAPI is on', () => { - runtime.config.featureToggles.foldersAppPlatformAPI = true; - const items = testFn(); - expect((items[1].item as DashboardViewItem).title).toBe('AppPlatform Folder'); + const sortedItemTitles = items.map((item) => (item.item as DashboardViewItem).title).sort(); + const expectedTitles = [folderA.item.title, folderB.item.title, folderC.item.title].sort(); + + expect(sortedItemTitles).toEqual(expectedTitles); + }); }); }); -function testFn() { - const { result } = renderHook(() => useFoldersQuery(true, {})); +async function testFn() { + const { result } = renderHook(() => useFoldersQuery(true, {}), { wrapper }); - expect(result.current.items).toEqual([getRootFolderItem()]); + expect(result.current.items[0]).toEqual(getRootFolderItem()); expect(result.current.isLoading).toBe(false); + act(() => { result.current.requestNextPage(undefined); }); - expect(result.current.items.length).toBe(2); + expect(result.current.isLoading).toBe(true); + + await waitFor(() => { + const withoutPaginationPlaceholders = result.current.items.filter((item) => item.item.kind !== 'ui'); + return expect(withoutPaginationPlaceholders.length).toBeGreaterThan(1); + }); + return result.current.items; } diff --git a/public/app/core/components/NestedFolderPicker/useFoldersQueryAppPlatform.ts b/public/app/core/components/NestedFolderPicker/useFoldersQueryAppPlatform.ts index 4828417c10c..b877c69aba0 100644 --- a/public/app/core/components/NestedFolderPicker/useFoldersQueryAppPlatform.ts +++ b/public/app/core/components/NestedFolderPicker/useFoldersQueryAppPlatform.ts @@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit'; import { QueryStatus } from '@reduxjs/toolkit/query'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { folderAPIv1beta1 } from 'app/api/clients/folder/v1beta1'; +import { dashboardAPIv0alpha1 } from 'app/api/clients/dashboard/v0alpha1'; import { DashboardViewItemWithUIItems, DashboardsTreeItem } from 'app/features/browse-dashboards/types'; import { useDispatch, useSelector } from 'app/types/store'; @@ -12,7 +12,7 @@ import { getPaginationPlaceholders } from '../../../features/browse-dashboards/s import { getRootFolderItem } from './utils'; -type GetFolderChildrenQuery = ReturnType>; +type GetFolderChildrenQuery = ReturnType>; type GetFolderChildrenRequest = { unsubscribe: () => void; }; @@ -32,9 +32,9 @@ export function useFoldersQueryAppPlatform(isBrowsing: boolean, openFolders: Rec const requestsRef = useRef([]); // Keep a list of selectors for dynamic state selection - const [selectors, setSelectors] = useState< - Array> - >([]); + const [selectors, setSelectors] = useState>>( + [] + ); // This is an aggregated dynamic selector of all the selectors for all the request issued while loading the folder // tree and returns the whole tree that was loaded so far. @@ -50,7 +50,7 @@ export function useFoldersQueryAppPlatform(isBrowsing: boolean, openFolders: Rec isLoading = true; } - const parentName = response.originalArgs?.name; + const parentName = response.originalArgs?.folder; if (parentName) { responseByParent[parentName] = response; } @@ -77,13 +77,13 @@ export function useFoldersQueryAppPlatform(isBrowsing: boolean, openFolders: Rec return; } - const args = { name: finalParentUid }; + const args = { folder: finalParentUid, type: 'folder' }; // Make a request - const subscription = dispatch(folderAPIv1beta1.endpoints.getFolderChildren.initiate(args)); + const subscription = dispatch(dashboardAPIv0alpha1.endpoints.getSearch.initiate(args)); // Add selector for the response to the list so we can then have an aggregated selector for all the folders - const selector = folderAPIv1beta1.endpoints.getFolderChildren.select(args); + const selector = dashboardAPIv0alpha1.endpoints.getSearch.select(args); setSelectors((selectors) => selectors.concat(selector)); // the subscriptions are saved in a ref so they can be unsubscribed on unmount @@ -113,18 +113,18 @@ export function useFoldersQueryAppPlatform(isBrowsing: boolean, openFolders: Rec response: GetFolderChildrenQuery | undefined, level: number ): Array> { - let folders = response?.data?.items ? [...response.data.items] : []; - folders.sort((a, b) => collator.compare(a.spec.title, b.spec.title)); + let folders = response?.data?.hits ? [...response.data.hits] : []; + folders.sort((a, b) => collator.compare(a.title, b.title)); const list = folders.flatMap((item) => { - const name = item.metadata.name!; + const name = item.name; const folderIsOpen = openFolders[name]; const flatItem: DashboardsTreeItem = { isOpen: Boolean(folderIsOpen), level: level, item: { kind: 'folder' as const, - title: item.spec.title, + title: item.title, // We use resource name as UID because well, not sure what metadata.uid would be used for now as you cannot // query by it. uid: name, diff --git a/public/app/core/components/Select/SortPicker.tsx b/public/app/core/components/Select/SortPicker.tsx index 4a0c635da2a..2b741cc1230 100644 --- a/public/app/core/components/Select/SortPicker.tsx +++ b/public/app/core/components/Select/SortPicker.tsx @@ -13,13 +13,14 @@ export interface Props { getSortOptions?: () => Promise; filter?: string[]; isClearable?: boolean; + disabled?: boolean; } const defaultSortOptionsGetter = (): Promise => { return getGrafanaSearcher().getSortOptions(); }; -export function SortPicker({ onChange, value, placeholder, filter, getSortOptions, isClearable }: Props) { +export function SortPicker({ onChange, value, placeholder, filter, getSortOptions, isClearable, disabled }: Props) { // Using sync Select and manual options fetching here since we need to find the selected option by value const options = useAsync<() => Promise>(async () => { const vals = await (getSortOptions ?? defaultSortOptionsGetter)(); @@ -45,6 +46,7 @@ export function SortPicker({ onChange, value, placeholder, filter, getSortOption placeholder={placeholder ?? `Sort (Default ${DEFAULT_SORT.label})`} prefix={} isClearable={isClearable} + disabled={disabled} /> ); } diff --git a/public/app/core/components/TagFilter/TagFilter.tsx b/public/app/core/components/TagFilter/TagFilter.tsx index 5301e128716..ad734018f9d 100644 --- a/public/app/core/components/TagFilter/TagFilter.tsx +++ b/public/app/core/components/TagFilter/TagFilter.tsx @@ -26,6 +26,7 @@ export interface Props { tagOptions: () => Promise; tags: string[]; width?: number; + disabled?: boolean; } const filterOption = (option: SelectableValue, searchQuery: string) => { @@ -44,6 +45,7 @@ export const TagFilter = ({ tagOptions, tags, width, + disabled, }: Props) => { const styles = useStyles2(getStyles); @@ -155,7 +157,7 @@ export const TagFilter = ({ return (
{isClearable && tags.length > 0 && ( - )} @@ -164,6 +166,7 @@ export const TagFilter = ({ {...selectOptions} prefix={} aria-label={t('tag-filter.select-aria-label', 'Tag filter')} + disabled={disabled} />
); diff --git a/public/app/core/components/TimePicker/TimePickerWithHistory.tsx b/public/app/core/components/TimePicker/TimePickerWithHistory.tsx index f866691ede0..54b9cedf608 100644 --- a/public/app/core/components/TimePicker/TimePickerWithHistory.tsx +++ b/public/app/core/components/TimePicker/TimePickerWithHistory.tsx @@ -1,12 +1,10 @@ import { uniqBy } from 'lodash'; -import { AppEvents, DateTime, TimeRange, isDateTime, rangeUtil } from '@grafana/data'; +import { AppEvents, DateTime, LocalStorageValueProvider, TimeRange, isDateTime, rangeUtil } from '@grafana/data'; import { t } from '@grafana/i18n'; import { TimeRangePickerProps, TimeRangePicker } from '@grafana/ui'; import appEvents from 'app/core/app_events'; -import { LocalStorageValueProvider } from '../LocalStorageValueProvider'; - const LOCAL_STORAGE_KEY = 'grafana.dashboard.timepicker.history'; const MAX_HISTORY_ITEMS = 4; diff --git a/public/app/core/components/WithFeatureToggle.tsx b/public/app/core/components/WithFeatureToggle.tsx deleted file mode 100644 index 9f24edb36f0..00000000000 --- a/public/app/core/components/WithFeatureToggle.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as React from 'react'; - -export interface Props { - featureToggle: boolean; -} - -export const WithFeatureToggle = ({ featureToggle, children }: React.PropsWithChildren) => { - if (featureToggle === true) { - return <>{children}; - } - - return null; -}; diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 80421c5d2f3..7adfd4242b7 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -4,6 +4,5 @@ import appEvents from './app_events'; import { profiler } from './profiler'; import { contextSrv } from './services/context_srv'; import TimeSeries, { updateLegendValues } from './time_series2'; -import { assignModelProperties } from './utils/model_utils'; -export { profiler, appEvents, colors, assignModelProperties, contextSrv, JsonExplorer, TimeSeries, updateLegendValues }; +export { profiler, appEvents, colors, contextSrv, JsonExplorer, TimeSeries, updateLegendValues }; diff --git a/public/app/core/icons/cached.json b/public/app/core/icons/cached.json index ce86bef4eae..810e2676755 100644 --- a/public/app/core/icons/cached.json +++ b/public/app/core/icons/cached.json @@ -74,6 +74,8 @@ "unicons/file-alt", "unicons/file-blank", "unicons/filter", + "unicons/filter-plus", + "unicons/filter-minus", "unicons/folder", "unicons/folder-open", "unicons/folder-plus", diff --git a/public/app/core/lodash_extended.ts b/public/app/core/lodash_extended.ts deleted file mode 100644 index f62be00e98c..00000000000 --- a/public/app/core/lodash_extended.ts +++ /dev/null @@ -1,12 +0,0 @@ -// eslint-disable-next-line lodash/import-scope -import _ from 'lodash'; - -/* - Mixins :) -*/ -_.mixin({ - move: (array, fromIndex, toIndex) => { - array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]); - return array; - }, -}); diff --git a/public/app/core/reducers/root.ts b/public/app/core/reducers/root.ts index 67078fdbd27..bc296ee14b6 100644 --- a/public/app/core/reducers/root.ts +++ b/public/app/core/reducers/root.ts @@ -2,6 +2,7 @@ import { ReducersMapObject } from '@reduxjs/toolkit'; import { AnyAction, combineReducers } from 'redux'; import { alertingAPI as alertingPackageAPI } from '@grafana/alerting/unstable'; +import { dashboardAPIv0alpha1 } from 'app/api/clients/dashboard/v0alpha1'; import sharedReducers from 'app/core/reducers'; import ldapReducers from 'app/features/admin/state/reducers'; import alertingReducers from 'app/features/alerting/state/reducers'; @@ -71,6 +72,7 @@ const rootReducers = { [provisioningAPIv0alpha1.reducerPath]: provisioningAPIv0alpha1.reducer, [folderAPIv1beta1.reducerPath]: folderAPIv1beta1.reducer, [advisorAPIv0alpha1.reducerPath]: advisorAPIv0alpha1.reducer, + [dashboardAPIv0alpha1.reducerPath]: dashboardAPIv0alpha1.reducer, // PLOP_INJECT_REDUCER // Used by the API client generator }; diff --git a/public/app/core/services/mocks/search_srv.ts b/public/app/core/services/mocks/search_srv.ts deleted file mode 100644 index 491e53b69c4..00000000000 --- a/public/app/core/services/mocks/search_srv.ts +++ /dev/null @@ -1,22 +0,0 @@ -export const mockSearch = jest.fn(() => { - return Promise.resolve([]); -}); - -export const mockGetDashboardTags = jest.fn(async () => - Promise.resolve([ - { term: 'tag1', count: 2 }, - { term: 'tag2', count: 10 }, - ]) -); - -export const mockGetSortOptions = jest.fn(() => - Promise.resolve({ sortOptions: [{ name: 'test', displayName: 'Test' }] }) -); - -export const SearchSrv = jest.fn().mockImplementation(() => { - return { - search: mockSearch, - getDashboardTags: mockGetDashboardTags, - getSortOptions: mockGetSortOptions, - }; -}); diff --git a/public/app/core/utils/model_utils.ts b/public/app/core/utils/model_utils.ts deleted file mode 100644 index d0e5a40e479..00000000000 --- a/public/app/core/utils/model_utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -export function assignModelProperties( - target: Record, - source: Record, - defaults: Record, - removeDefaults?: undefined -) { - for (const key in defaults) { - if (!defaults.hasOwnProperty(key)) { - continue; - } - - target[key] = source[key] === undefined ? defaults[key] : source[key]; - } -} diff --git a/public/app/features/alerting/unified/rule-list/GrafanaRuleListItem.tsx b/public/app/features/alerting/unified/rule-list/GrafanaRuleListItem.tsx index 5667069082b..ed90116bcaa 100644 --- a/public/app/features/alerting/unified/rule-list/GrafanaRuleListItem.tsx +++ b/public/app/features/alerting/unified/rule-list/GrafanaRuleListItem.tsx @@ -53,6 +53,7 @@ export function GrafanaRuleListItem({ isPaused: rule?.isPaused, application: 'grafana' as const, actions: , + querySourceUIDs: rule?.queriedDatasourceUIDs, }; if (prometheusRuleType.grafana.alertingRule(rule)) { diff --git a/public/app/features/alerting/unified/rule-list/PaginatedGrafanaLoader.tsx b/public/app/features/alerting/unified/rule-list/PaginatedGrafanaLoader.tsx index 4e64afae512..296535b881a 100644 --- a/public/app/features/alerting/unified/rule-list/PaginatedGrafanaLoader.tsx +++ b/public/app/features/alerting/unified/rule-list/PaginatedGrafanaLoader.tsx @@ -77,7 +77,7 @@ function PaginatedGroupsLoader({ groupFilter, namespaceFilter }: LoaderProps) { return ( { @@ -75,6 +76,7 @@ export const AlertRuleListItem = (props: AlertRuleListItemProps) => { actions = null, operation, showLocation = true, + querySourceUIDs = [], } = props; const listItemAriaId = useId(); @@ -94,6 +96,10 @@ export const AlertRuleListItem = (props: AlertRuleListItemProps) => { ); } + if (querySourceUIDs.length > 0) { + metadata.push(); + } + if (!isPaused) { if (lastEvaluation && evaluationInterval) { metadata.push( @@ -179,6 +185,7 @@ export function RecordingRuleListItem({ origin, actions, showLocation = true, + querySourceUIDs = [], }: RecordingRuleListItemProps) { const metadata: ReactNode[] = []; if (namespace && group && showLocation) { @@ -195,6 +202,10 @@ export function RecordingRuleListItem({ ); } + if (querySourceUIDs.length > 0) { + metadata.push(); + } + return ( ds !== undefined); + + return ( + + {dataSources.map((dataSource) => { + return ( + + + + ); + })} + + ); +}); + function RuleLabels({ labels }: { labels: Labels }) { const styles = useStyles2(getStyles); @@ -417,3 +451,33 @@ export type RuleListItemCommonProps = Pick< AlertRuleListItemProps, Extract >; + +interface DataSourceLogoProps { + dataSource: DataSourceInstanceSettings; +} + +const DataSourceLogo = forwardRef(({ dataSource }, ref) => { + const styles = useStyles2(dataSourceLogoStyles); + + return ( + {`${dataSource.meta.name} + ); +}); + +const dataSourceLogoStyles = (theme: GrafanaTheme2) => ({ + logo: css({ + height: '14px', + width: '14px', + borderRadius: theme.shape.radius.default, + }), + filter: css({ + filter: `invert(${theme.isLight ? 1 : 0})`, + }), +}); diff --git a/public/app/features/alerting/unified/rule-list/components/ListItem.tsx b/public/app/features/alerting/unified/rule-list/components/ListItem.tsx index ce3ea7f6fb6..0e15e5fb696 100644 --- a/public/app/features/alerting/unified/rule-list/components/ListItem.tsx +++ b/public/app/features/alerting/unified/rule-list/components/ListItem.tsx @@ -39,7 +39,7 @@ export const ListItem = (props: ListItemProps) => { {/* metadata */} - + {meta?.map((item, index) => ( {index > 0 && } @@ -72,7 +72,7 @@ export const SkeletonListItem = () => { const Separator = () => ( - {'·'} + {'|'} ); diff --git a/public/app/features/alerting/unified/rule-list/hooks/filters.ts b/public/app/features/alerting/unified/rule-list/hooks/filters.ts index 3602081e7c0..2e9535dd97f 100644 --- a/public/app/features/alerting/unified/rule-list/hooks/filters.ts +++ b/public/app/features/alerting/unified/rule-list/hooks/filters.ts @@ -22,12 +22,10 @@ export function groupFilter( const { name, file } = group; const { namespace, groupName } = filterState; - // Use fuzzy search for namespace if (namespace && !fuzzyMatches(file, namespace)) { return false; } - // Use fuzzy search for group name if (groupName && !fuzzyMatches(name, groupName)) { return false; } @@ -41,17 +39,17 @@ export function groupFilter( export function ruleFilter(rule: PromRuleDTO, filterState: RulesFilter) { const { name, labels = {}, health, type } = rule; - // Free form words filter (uses fuzzy matching for each word) - if (filterState.freeFormWords.length > 0 && !filterState.freeFormWords.some((word) => fuzzyMatches(name, word))) { - return false; + if (filterState.freeFormWords.length > 0) { + const nameMatches = fuzzyMatches(name, filterState.freeFormWords.join(' ')); + if (!nameMatches) { + return false; + } } - // Rule name filter (uses fuzzy matching) if (filterState.ruleName && !fuzzyMatches(name, filterState.ruleName)) { return false; } - // Labels filter if (filterState.labels.length > 0) { const matchers = compact(filterState.labels.map(looseParseMatcher)); const doRuleLabelsMatchQuery = matchers.length > 0 && labelsMatchMatchers(labels, matchers); @@ -68,12 +66,10 @@ export function ruleFilter(rule: PromRuleDTO, filterState: RulesFilter) { } } - // Rule type filter if (filterState.ruleType && type !== filterState.ruleType) { return false; } - // Rule state filter (for alerting rules only) if (filterState.ruleState) { if (!prometheusRuleType.alertingRule(rule)) { return false; @@ -83,7 +79,6 @@ export function ruleFilter(rule: PromRuleDTO, filterState: RulesFilter) { } } - // Rule health filter if (filterState.ruleHealth && health !== filterState.ruleHealth) { return false; } @@ -102,7 +97,6 @@ export function ruleFilter(rule: PromRuleDTO, filterState: RulesFilter) { } } - // Dashboard UID filter if (filterState.dashboardUid) { if (!prometheusRuleType.alertingRule(rule)) { return false; diff --git a/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx b/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx index 4a2c9f4a82b..a9a0dda6274 100644 --- a/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx +++ b/public/app/features/browse-dashboards/BrowseDashboardsPage.test.tsx @@ -1,7 +1,6 @@ import { render as rtlRender, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { HttpResponse, http } from 'msw'; -import { setupServer, SetupServer } from 'msw/node'; import { ComponentProps } from 'react'; import * as React from 'react'; import { useParams } from 'react-router-dom-v5-compat'; @@ -9,13 +8,16 @@ import AutoSizer from 'react-virtualized-auto-sizer'; import { TestProvider } from 'test/helpers/TestProvider'; import { selectors } from '@grafana/e2e-selectors'; +import server, { setupMockServer } from '@grafana/test-utils/server'; +import { getFolderFixtures } from '@grafana/test-utils/unstable'; import { contextSrv } from 'app/core/core'; import { backendSrv } from 'app/core/services/backend_srv'; import BrowseDashboardsPage from './BrowseDashboardsPage'; -import { wellFormedTree } from './fixtures/dashboardsTreeItem.fixture'; import * as permissions from './permissions'; -const [mockTree, { dashbdD, folderA, folderA_folderA }] = wellFormedTree(); + +setupMockServer(); +const [mockTree, { dashbdD, folderA, folderA_folderA }] = getFolderFixtures(); jest.mock('@grafana/runtime', () => ({ ...jest.requireActual('@grafana/runtime'), @@ -111,7 +113,6 @@ jest.mock('app/features/browse-dashboards/api/services', () => { }); describe('browse-dashboards BrowseDashboardsPage', () => { - let server: SetupServer; const mockPermissions = { canCreateDashboards: true, canEditDashboards: true, @@ -123,33 +124,14 @@ describe('browse-dashboards BrowseDashboardsPage', () => { canDeleteDashboards: true, }; - beforeAll(() => { - server = setupServer( - http.get('/api/folders/:uid', () => { - return HttpResponse.json({ - title: folderA.item.title, - uid: folderA.item.uid, - }); - }), - http.get('/api/search', () => { - return HttpResponse.json({}); - }), + beforeEach(() => { + server.use( http.get('/api/search/sorting', () => { return HttpResponse.json({ sortOptions: [], }); - }), - http.get('/apis/provisioning.grafana.app/v0alpha1/namespaces/default/settings', () => { - return HttpResponse.json({ - items: [], - }); }) ); - server.listen(); - }); - - afterAll(() => { - server.close(); }); beforeEach(() => { @@ -170,7 +152,6 @@ describe('browse-dashboards BrowseDashboardsPage', () => { canDeleteDashboards: true, }); jest.restoreAllMocks(); - server.resetHandlers(); }); describe('at the root level', () => { diff --git a/public/app/features/browse-dashboards/BrowseFolderLibraryPanelsPage.test.tsx b/public/app/features/browse-dashboards/BrowseFolderLibraryPanelsPage.test.tsx index c60a8a8de56..9b96316add6 100644 --- a/public/app/features/browse-dashboards/BrowseFolderLibraryPanelsPage.test.tsx +++ b/public/app/features/browse-dashboards/BrowseFolderLibraryPanelsPage.test.tsx @@ -1,9 +1,9 @@ -import { render as rtlRender, screen } from '@testing-library/react'; import { http, HttpResponse } from 'msw'; -import { SetupServer, setupServer } from 'msw/node'; import { useParams } from 'react-router-dom-v5-compat'; -import { TestProvider } from 'test/helpers/TestProvider'; +import { render, screen } from 'test/test-utils'; +import server, { setupMockServer } from '@grafana/test-utils/server'; +import { getFolderFixtures } from '@grafana/test-utils/unstable'; import { contextSrv } from 'app/core/core'; import { backendSrv } from 'app/core/services/backend_srv'; @@ -11,10 +11,7 @@ import BrowseFolderLibraryPanelsPage from './BrowseFolderLibraryPanelsPage'; import { getLibraryElementsResponse } from './fixtures/libraryElements.fixture'; import * as permissions from './permissions'; -function render(...[ui, options]: Parameters) { - rtlRender({ui}, options); -} - +setupMockServer(); jest.mock('@grafana/runtime', () => ({ ...jest.requireActual('@grafana/runtime'), getBackendSrv: () => backendSrv, @@ -28,15 +25,15 @@ jest.mock('react-router-dom-v5-compat', () => ({ useParams: jest.fn(), })); -const mockFolderName = 'myFolder'; -const mockFolderUid = '12345'; +const [_, { folderA }] = getFolderFixtures(); +const mockFolderName = folderA.item.title; +const mockFolderUid = folderA.item.uid; const mockLibraryElementsResponse = getLibraryElementsResponse(1, { folderUid: mockFolderUid, }); describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => { (useParams as jest.Mock).mockReturnValue({ uid: mockFolderUid }); - let server: SetupServer; const mockPermissions = { canCreateDashboards: true, canEditDashboards: true, @@ -48,14 +45,8 @@ describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => { canDeleteDashboards: true, }; - beforeAll(() => { - server = setupServer( - http.get('/api/folders/:uid', () => { - return HttpResponse.json({ - title: mockFolderName, - uid: mockFolderUid, - }); - }), + beforeEach(() => { + server.use( http.get('/api/library-elements', () => { return HttpResponse.json({ result: mockLibraryElementsResponse, @@ -65,11 +56,6 @@ describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => { return HttpResponse.json({}); }) ); - server.listen(); - }); - - afterAll(() => { - server.close(); }); beforeEach(() => { @@ -79,7 +65,6 @@ describe('browse-dashboards BrowseFolderLibraryPanelsPage', () => { afterEach(() => { jest.restoreAllMocks(); - server.resetHandlers(); }); it('displays the folder title', async () => { diff --git a/public/app/features/browse-dashboards/components/BrowseActions/MoveModal.test.tsx b/public/app/features/browse-dashboards/components/BrowseActions/MoveModal.test.tsx index fb1a160d8b1..c941cd76150 100644 --- a/public/app/features/browse-dashboards/components/BrowseActions/MoveModal.test.tsx +++ b/public/app/features/browse-dashboards/components/BrowseActions/MoveModal.test.tsx @@ -1,38 +1,26 @@ -import userEvent from '@testing-library/user-event'; import { HttpResponse, http } from 'msw'; -import { SetupServer, setupServer } from 'msw/node'; import { render, screen } from 'test/test-utils'; +import { setBackendSrv } from '@grafana/runtime'; +import server, { setupMockServer } from '@grafana/test-utils/server'; +import { getFolderFixtures } from '@grafana/test-utils/unstable'; import { backendSrv } from 'app/core/services/backend_srv'; -import { treeViewersCanEdit, wellFormedTree } from '../../fixtures/dashboardsTreeItem.fixture'; - import { MoveModal, Props } from './MoveModal'; -const [mockTree, { folderA }] = wellFormedTree(); -const [mockTreeThatViewersCanEdit /* shares folders with wellFormedTree */] = treeViewersCanEdit(); +const [_, { folderA }] = getFolderFixtures(); -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - getBackendSrv: () => backendSrv, -})); +setBackendSrv(backendSrv); +setupMockServer(); describe('browse-dashboards MoveModal', () => { const mockOnDismiss = jest.fn(); const mockOnConfirm = jest.fn(); let props: Props; - let server: SetupServer; window.HTMLElement.prototype.scrollIntoView = () => {}; - beforeAll(() => { - server = setupServer( - http.get('/api/folders/:uid', () => { - return HttpResponse.json({ - title: folderA.item.title, - uid: folderA.item.uid, - }); - }), - + beforeEach(() => { + server.use( http.get('/api/folders/:uid/counts', () => { return HttpResponse.json({ folder: 1, @@ -40,43 +28,9 @@ describe('browse-dashboards MoveModal', () => { librarypanel: 3, alertrule: 4, }); - }), - - http.get('/apis/provisioning.grafana.app/v0alpha1/namespaces/default/settings', () => { - return HttpResponse.json({ - items: [], - }); - }), - - http.get('/api/folders', ({ request }) => { - const url = new URL(request.url); - const parentUid = url.searchParams.get('parentUid') ?? undefined; - const permission = url.searchParams.get('permission'); - - const limit = parseInt(url.searchParams.get('limit') ?? '1000', 10); - const page = parseInt(url.searchParams.get('page') ?? '1', 10); - - const tree = permission === 'Edit' ? mockTreeThatViewersCanEdit : mockTree; - - // reconstruct a folder API response from the flat tree fixture - const folders = tree - .filter((v) => v.item.kind === 'folder' && v.item.parentUID === parentUid) - .map((folder) => { - return { - uid: folder.item.uid, - title: folder.item.kind === 'folder' ? folder.item.title : "invalid - this shouldn't happen", - }; - }) - .slice(limit * (page - 1), limit * page); - - return HttpResponse.json(folders); }) ); - server.listen(); - }); - - beforeEach(() => { props = { isOpen: true, onConfirm: mockOnConfirm, @@ -90,10 +44,6 @@ describe('browse-dashboards MoveModal', () => { }; }); - afterAll(() => { - server.close(); - }); - it('renders a dialog with the correct title', async () => { render(); @@ -130,36 +80,36 @@ describe('browse-dashboards MoveModal', () => { }); it('enables the `Move` button once a folder is selected', async () => { - render(); + const { user } = render(); expect(await screen.findByRole('button', { name: 'Move' })).toBeDisabled(); // Open the picker and wait for children to load const folderPicker = await screen.findByRole('button', { name: 'Select folder' }); - await userEvent.click(folderPicker); + await user.click(folderPicker); await screen.findByLabelText(folderA.item.title); // Select the folder - await userEvent.click(screen.getByLabelText(folderA.item.title)); + await user.click(screen.getByLabelText(folderA.item.title)); const moveButton = await screen.findByRole('button', { name: 'Move' }); expect(moveButton).toBeEnabled(); - await userEvent.click(moveButton); + await user.click(moveButton); expect(mockOnConfirm).toHaveBeenCalledWith(folderA.item.uid); }); it('calls onDismiss when clicking the `Cancel` button', async () => { - render(); + const { user } = render(); - await userEvent.click(await screen.findByRole('button', { name: 'Cancel' })); + await user.click(await screen.findByRole('button', { name: 'Cancel' })); expect(mockOnDismiss).toHaveBeenCalled(); }); it('calls onDismiss when clicking the X', async () => { - render(); + const { user } = render(); - await userEvent.click(await screen.findByRole('button', { name: 'Close' })); + await user.click(await screen.findByRole('button', { name: 'Close' })); expect(mockOnDismiss).toHaveBeenCalled(); }); }); diff --git a/public/app/features/browse-dashboards/components/BrowseView.test.tsx b/public/app/features/browse-dashboards/components/BrowseView.test.tsx index 1a19d2b5b61..929f5bbf53c 100644 --- a/public/app/features/browse-dashboards/components/BrowseView.test.tsx +++ b/public/app/features/browse-dashboards/components/BrowseView.test.tsx @@ -3,14 +3,13 @@ import userEvent from '@testing-library/user-event'; import { TestProvider } from 'test/helpers/TestProvider'; import { selectors } from '@grafana/e2e-selectors'; +import { getFolderFixtures } from '@grafana/test-utils/unstable'; import { DashboardViewItem } from 'app/features/search/types'; -import { wellFormedTree } from '../fixtures/dashboardsTreeItem.fixture'; - import { BrowseView } from './BrowseView'; const [mockTree, { folderA, folderA_folderA, folderA_folderB, folderA_folderB_dashbdB, dashbdD, folderB_empty }] = - wellFormedTree(); + getFolderFixtures(); function render(...[ui, options]: Parameters) { rtlRender({ui}, options); diff --git a/public/app/features/browse-dashboards/fixtures/dashboardsTreeItem.fixture.ts b/public/app/features/browse-dashboards/fixtures/dashboardsTreeItem.fixture.ts index 4ac6515c726..7b5d17a2825 100644 --- a/public/app/features/browse-dashboards/fixtures/dashboardsTreeItem.fixture.ts +++ b/public/app/features/browse-dashboards/fixtures/dashboardsTreeItem.fixture.ts @@ -1,5 +1,6 @@ import { Chance } from 'chance'; +import { getFolderFixtures } from '@grafana/test-utils/unstable'; import { DashboardViewItem } from 'app/features/search/types'; import { DashboardsTreeItem, UIDashboardViewItem } from '../types'; @@ -74,7 +75,7 @@ export function sharedWithMeFolder(seed = 1): DashboardsTreeItem 1 || partialUpdateKeys[0] !== '_renderCounter') { + return true; + } + } // SceneQueryRunner includes the DS configuration if (payload.changedObject instanceof SceneQueryRunner) { if (!Object.prototype.hasOwnProperty.call(payload.partialUpdate, 'data')) { diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index 662d92a32df..48581e303ae 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -88,6 +88,7 @@ import { DashboardGridItem } from './layout-default/DashboardGridItem'; import { DefaultGridLayoutManager } from './layout-default/DefaultGridLayoutManager'; import { addNewRowTo } from './layouts-shared/addNew'; import { clearClipboard } from './layouts-shared/paste'; +import { getIsLazy } from './layouts-shared/utils'; import { DashboardLayoutManager } from './types/DashboardLayoutManager'; import { LayoutParent } from './types/LayoutParent'; @@ -198,7 +199,7 @@ export class DashboardScene extends SceneObjectBase impleme meta: {}, editable: true, $timeRange: state.$timeRange ?? new SceneTimeRange({}), - body: state.body ?? DefaultGridLayoutManager.fromVizPanels(), + body: state.body ?? DefaultGridLayoutManager.fromVizPanels([], getIsLazy(state.preload)), links: state.links ?? [], ...state, editPane: new DashboardEditPane(), diff --git a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayout.tsx b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayout.tsx index be19f886d83..b96550cfa22 100644 --- a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayout.tsx +++ b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayout.tsx @@ -22,9 +22,6 @@ export interface AutoGridLayoutState extends SceneObjectState, AutoGridLayoutOpt */ md?: AutoGridLayoutOptions; - /** True when the items should be lazy loaded */ - isLazy?: boolean; - /** True when the items should be draggable */ isDraggable?: boolean; diff --git a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayoutRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayoutRenderer.tsx index 7f66994354d..2a44564b536 100644 --- a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayoutRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridLayoutRenderer.tsx @@ -1,4 +1,5 @@ import { css, cx } from '@emotion/css'; +import { useMemo } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { LazyLoader, SceneComponentProps, sceneGraph } from '@grafana/scenes'; @@ -8,18 +9,21 @@ import { useHasClonedParents } from '../../utils/clone'; import { useDashboardState } from '../../utils/utils'; import { CanvasGridAddActions } from '../layouts-shared/CanvasGridAddActions'; import { dashboardCanvasAddButtonHoverStyles } from '../layouts-shared/styles'; +import { getIsLazy } from '../layouts-shared/utils'; import { AutoGridLayout, AutoGridLayoutState } from './AutoGridLayout'; import { AutoGridLayoutManager } from './AutoGridLayoutManager'; export function AutoGridLayoutRenderer({ model }: SceneComponentProps) { - const { children, isHidden, isLazy } = model.useState(); + const { children, isHidden } = model.useState(); const hasClonedParents = useHasClonedParents(model); const styles = useStyles2(getStyles, model.state); - const { layoutOrchestrator, isEditing } = useDashboardState(model); + const { layoutOrchestrator, isEditing, preload } = useDashboardState(model); const layoutManager = sceneGraph.getAncestor(model, AutoGridLayoutManager); const { fillScreen } = layoutManager.useState(); + const isLazy = useMemo(() => getIsLazy(preload), [preload]); + if (isHidden || !layoutOrchestrator) { return null; } diff --git a/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx index b85cb2bcf4b..76fffddd0a5 100644 --- a/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-default/DefaultGridLayoutManager.tsx @@ -46,6 +46,7 @@ import { AutoGridItem } from '../layout-auto-grid/AutoGridItem'; import { CanvasGridAddActions } from '../layouts-shared/CanvasGridAddActions'; import { clearClipboard, getDashboardGridItemFromClipboard } from '../layouts-shared/paste'; import { dashboardCanvasAddButtonHoverStyles } from '../layouts-shared/styles'; +import { getIsLazy } from '../layouts-shared/utils'; import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; import { LayoutRegistryItem } from '../types/LayoutRegistryItem'; @@ -558,10 +559,11 @@ export class DefaultGridLayoutManager public static createFromLayout(currentLayout: DashboardLayoutManager): DefaultGridLayoutManager { const panels = currentLayout.getVizPanels(); - return DefaultGridLayoutManager.fromVizPanels(panels); + const isLazy = getIsLazy(getDashboardSceneFor(currentLayout).state.preload)!; + return DefaultGridLayoutManager.fromVizPanels(panels, isLazy); } - public static fromVizPanels(panels: VizPanel[] = []): DefaultGridLayoutManager { + public static fromVizPanels(panels: VizPanel[] = [], isLazy?: boolean | undefined): DefaultGridLayoutManager { const children: DashboardGridItem[] = []; const panelHeight = 10; const panelWidth = GRID_COLUMN_COUNT / 3; @@ -599,6 +601,7 @@ export class DefaultGridLayoutManager children: children, isDraggable: true, isResizable: true, + isLazy, }), }); } @@ -606,7 +609,8 @@ export class DefaultGridLayoutManager public static fromGridItems( gridItems: SceneGridItemLike[], isDraggable?: boolean, - isResizable?: boolean + isResizable?: boolean, + isLazy?: boolean | undefined ): DefaultGridLayoutManager { const children = gridItems.reduce((acc, gridItem) => { gridItem.clearParent(); @@ -620,6 +624,7 @@ export class DefaultGridLayoutManager children, isDraggable, isResizable, + isLazy, }), }); } diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx index b03b8b26e26..affa68d54ea 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx @@ -83,6 +83,7 @@ export function RowItemRenderer({ model }: SceneComponentProps) { setTimeout(() => onSelect?.(evt)); }} + data-testid={selectors.components.DashboardRow.wrapper(title!)} {...dragProvided.draggableProps} > {(!isHeaderHidden || isEditing) && ( diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx index 34b2a9e3a1a..1b04a9d6c23 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx @@ -258,7 +258,8 @@ export class RowsLayoutManager extends SceneObjectBase i layout: DefaultGridLayoutManager.fromGridItems( rowConfig.children, rowConfig.isDraggable ?? layout.state.grid.state.isDraggable, - rowConfig.isResizable ?? layout.state.grid.state.isResizable + rowConfig.isResizable ?? layout.state.grid.state.isResizable, + layout.state.grid.state.isLazy ), }) ); diff --git a/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts b/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts index 41402564081..eac9ff86333 100644 --- a/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts +++ b/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts @@ -1,6 +1,7 @@ import { useEffect, useRef } from 'react'; import { SceneObject } from '@grafana/scenes'; +import { contextSrv } from 'app/core/core'; import { DashboardLayoutManager, isDashboardLayoutManager } from '../types/DashboardLayoutManager'; import { isLayoutParent } from '../types/LayoutParent'; @@ -75,3 +76,8 @@ export function ungroupLayout(layout: DashboardLayoutManager, innerLayout: Dashb layoutParent.switchLayout(innerLayout); } } + +export function getIsLazy(preload: boolean | undefined): boolean { + // We don't want to lazy load panels in the case of image renderer + return !(preload || (contextSrv.user && contextSrv.user.authenticatedBy === 'render')); +} diff --git a/public/app/features/dashboard-scene/serialization/layoutSerializers/DefaultGridLayoutSerializer.ts b/public/app/features/dashboard-scene/serialization/layoutSerializers/DefaultGridLayoutSerializer.ts index 8688d1cbb08..e19059547b7 100644 --- a/public/app/features/dashboard-scene/serialization/layoutSerializers/DefaultGridLayoutSerializer.ts +++ b/public/app/features/dashboard-scene/serialization/layoutSerializers/DefaultGridLayoutSerializer.ts @@ -9,10 +9,10 @@ import { PanelKind, LibraryPanelKind, } from '@grafana/schema/dist/esm/schema/dashboard/v2'; -import { contextSrv } from 'app/core/core'; import { DashboardGridItem } from '../../scene/layout-default/DashboardGridItem'; import { DefaultGridLayoutManager } from '../../scene/layout-default/DefaultGridLayoutManager'; +import { getIsLazy } from '../../scene/layouts-shared/utils'; import { dashboardSceneGraph } from '../../utils/dashboardSceneGraph'; import { calculateGridItemDimensions, isLibraryPanel } from '../../utils/utils'; @@ -41,7 +41,7 @@ export function deserializeDefaultGridLayout( } return new DefaultGridLayoutManager({ grid: new SceneGridLayout({ - isLazy: !(preload || contextSrv.user.authenticatedBy === 'render'), + isLazy: getIsLazy(preload), children: createSceneGridLayoutForItems(layout, elements, panelIdGenerator), }), }); diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index b7af7080ea8..8657af91ba1 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -20,7 +20,6 @@ import { SceneObjectState, } from '@grafana/scenes'; import { isWeekStart } from '@grafana/ui'; -import { contextSrv } from 'app/core/core'; import { K8S_V1_DASHBOARD_API_CONFIG } from 'app/features/dashboard/api/v1'; import { getDashboardInteractionCallback, @@ -50,6 +49,7 @@ import { RowRepeaterBehavior } from '../scene/layout-default/RowRepeaterBehavior import { RowActions } from '../scene/layout-default/row-actions/RowActions'; import { RowItem } from '../scene/layout-rows/RowItem'; import { RowsLayoutManager } from '../scene/layout-rows/RowsLayoutManager'; +import { getIsLazy } from '../scene/layouts-shared/utils'; import { setDashboardPanelContext } from '../scene/setDashboardPanelContext'; import { DashboardLayoutManager } from '../scene/types/DashboardLayoutManager'; import { createPanelDataProvider } from '../utils/createPanelDataProvider'; @@ -326,7 +326,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel, } else { body = new DefaultGridLayoutManager({ grid: new SceneGridLayout({ - isLazy: !(dto.preload || contextSrv.user.authenticatedBy === 'render'), + isLazy: getIsLazy(dto.preload), children: createSceneObjectsForPanels(oldModel.panels), }), }); diff --git a/public/app/features/dashboard/components/TransformationsEditor/TransformationPicker.tsx b/public/app/features/dashboard/components/TransformationsEditor/TransformationPicker.tsx index 23a94ffe8cb..6f2d0f03450 100644 --- a/public/app/features/dashboard/components/TransformationsEditor/TransformationPicker.tsx +++ b/public/app/features/dashboard/components/TransformationsEditor/TransformationPicker.tsx @@ -1,11 +1,10 @@ import { css } from '@emotion/css'; import { FormEventHandler, KeyboardEventHandler, ReactNode } from 'react'; -import { DocsId, GrafanaTheme2, TransformerRegistryItem } from '@grafana/data'; +import { DocsId, GrafanaTheme2, LocalStorageValueProvider, TransformerRegistryItem } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { Trans, t } from '@grafana/i18n'; import { Card, Container, VerticalGroup, Alert, Input, useStyles2 } from '@grafana/ui'; -import { LocalStorageValueProvider } from 'app/core/components/LocalStorageValueProvider'; import { getDocsLink } from 'app/core/utils/docsLinks'; import { PluginStateInfo } from 'app/features/plugins/components/PluginStateInfo'; diff --git a/public/app/features/dashboard/state/DashboardMigrator.test.ts b/public/app/features/dashboard/state/DashboardMigrator.test.ts index 00ba2415455..e82c2a723ba 100644 --- a/public/app/features/dashboard/state/DashboardMigrator.test.ts +++ b/public/app/features/dashboard/state/DashboardMigrator.test.ts @@ -1846,6 +1846,16 @@ describe('DashboardModel', () => { }, ], }, + // @ts-expect-error + { + id: 7, + datasource: { type: 'prometheus', uid: 'prom-uid' }, + }, + // @ts-expect-error + { + id: 8, + datasource: { type: 'prometheus' }, + }, ], }); }); @@ -1875,6 +1885,14 @@ describe('DashboardModel', () => { it('should update datasources in panels collapsed rows', () => { expect(model.panels[3].panels?.[0].datasource).toEqual({ type: 'prometheus', uid: 'prom-uid' }); }); + + it("should not migrate datasource if it's already a ref", () => { + expect(model.panels[4].datasource).toEqual({ type: 'prometheus', uid: 'prom-uid' }); + }); + + it("should not migrate datasource if it's already a ref with only a type", () => { + expect(model.panels[5].datasource).toEqual({ type: 'prometheus' }); + }); }); describe('when fixing query and panel data source refs out of sync due to default data source change', () => { diff --git a/public/app/features/datasources/components/CloudInfoBox.tsx b/public/app/features/datasources/components/CloudInfoBox.tsx index 10f1d3c2146..c274f91e121 100644 --- a/public/app/features/datasources/components/CloudInfoBox.tsx +++ b/public/app/features/datasources/components/CloudInfoBox.tsx @@ -1,8 +1,7 @@ -import { DataSourceSettings } from '@grafana/data'; +import { DataSourceSettings, LocalStorageValueProvider } from '@grafana/data'; import { GrafanaEdition } from '@grafana/data/internal'; import { Trans, t } from '@grafana/i18n'; import { Alert, TextLink } from '@grafana/ui'; -import { LocalStorageValueProvider } from 'app/core/components/LocalStorageValueProvider'; import { config } from 'app/core/config'; const LOCAL_STORAGE_KEY = 'datasources.settings.cloudInfoBox.isDismissed'; diff --git a/public/app/features/datasources/hooks.test.ts b/public/app/features/datasources/hooks.test.ts new file mode 100644 index 00000000000..a36f8ac2e8b --- /dev/null +++ b/public/app/features/datasources/hooks.test.ts @@ -0,0 +1,114 @@ +import { renderHook, act } from '@testing-library/react'; + +import { TestDataSettings } from '../query/state/mocks/mockDataSource'; + +import { useRecentlyUsedDataSources } from './hooks'; + +// Mock react-use's useLocalStorage +jest.mock('react-use', () => ({ + useLocalStorage: jest.fn(), +})); + +const mockUseLocalStorage = jest.requireMock('react-use').useLocalStorage; + +describe('useRecentlyUsedDataSources', () => { + let mockSetStorage: jest.Mock; + + beforeEach(() => { + jest.clearAllMocks(); + mockSetStorage = jest.fn(); + + // Default mock implementation + mockUseLocalStorage.mockReturnValue([[], mockSetStorage]); + }); + + describe('basic functionality', () => { + it('should return an array and a function', () => { + const { result } = renderHook(() => useRecentlyUsedDataSources()); + + expect(Array.isArray(result.current[0])).toBe(true); + expect(typeof result.current[1]).toBe('function'); + }); + + it('should return stored values from local storage', () => { + const storedValues = ['uid1', 'uid2', 'uid3']; + mockUseLocalStorage.mockReturnValue([storedValues, mockSetStorage]); + + const { result } = renderHook(() => useRecentlyUsedDataSources()); + + expect(result.current[0]).toEqual(storedValues); + }); + }); + + describe('adding data sources', () => { + it('should add a new data source to an empty list', () => { + const { result } = renderHook(() => useRecentlyUsedDataSources()); + const dataSource = { ...TestDataSettings, uid: 'test-uid' }; + + act(() => { + result.current[1](dataSource); + }); + + expect(mockSetStorage).toHaveBeenCalledWith(['test-uid']); + }); + + it('should add a new data source to the end of existing list', () => { + const existingValues = ['uid1', 'uid2']; + mockUseLocalStorage.mockReturnValue([existingValues, mockSetStorage]); + + const { result } = renderHook(() => useRecentlyUsedDataSources()); + const dataSource = { ...TestDataSettings, uid: 'test-uid' }; + + act(() => { + result.current[1](dataSource); + }); + + expect(mockSetStorage).toHaveBeenCalledWith(['uid1', 'uid2', 'test-uid']); + }); + + it('should not store built-in data sources', () => { + const { result } = renderHook(() => useRecentlyUsedDataSources()); + const builtInDataSource = { ...TestDataSettings, meta: { ...TestDataSettings.meta, builtIn: true } }; + + act(() => { + result.current[1](builtInDataSource); + }); + + expect(mockSetStorage).not.toHaveBeenCalled(); + }); + }); + + describe('duplicate handling', () => { + it('should move existing data source to the end when adding duplicate', () => { + const existingValues = ['uid1', 'test-uid', 'uid3']; + mockUseLocalStorage.mockReturnValue([existingValues, mockSetStorage]); + + const { result } = renderHook(() => useRecentlyUsedDataSources()); + const dataSource = { ...TestDataSettings, uid: 'test-uid' }; + + act(() => { + result.current[1](dataSource); + }); + + // uid2 should be moved to the end, others should maintain order + expect(mockSetStorage).toHaveBeenCalledWith(['uid1', 'uid3', 'test-uid']); + }); + }); + + describe('maximum items limit', () => { + it('should limit the array to 5 items when adding to a full list', () => { + const existingValues = ['uid1', 'uid2', 'uid3', 'uid4', 'uid5']; + mockUseLocalStorage.mockReturnValue([existingValues, mockSetStorage]); + + const { result } = renderHook(() => useRecentlyUsedDataSources()); + const dataSource = { ...TestDataSettings, uid: 'test-uid' }; + + act(() => { + result.current[1](dataSource); + }); + + // Should remove the first item and add new one at the end + expect(mockSetStorage).toHaveBeenCalledWith(['uid2', 'uid3', 'uid4', 'uid5', 'test-uid']); + }); + }); +}); diff --git a/public/app/features/datasources/hooks.ts b/public/app/features/datasources/hooks.ts index 3a08d716cb2..73c356c4ff8 100644 --- a/public/app/features/datasources/hooks.ts +++ b/public/app/features/datasources/hooks.ts @@ -28,7 +28,12 @@ export function useRecentlyUsedDataSources(): [string[], (ds: DataSourceInstance ); setStorage([...value, ds.uid]); } else { - setStorage([...value, ds.uid].slice(1, 6)); + const newArray = [...value, ds.uid]; + if (newArray.length > 5) { + setStorage(newArray.slice(1, 6)); + } else { + setStorage(newArray); + } } }, [value, setStorage] diff --git a/public/app/features/explore/Logs/Logs.tsx b/public/app/features/explore/Logs/Logs.tsx index a6d4040a446..fbac705c277 100644 --- a/public/app/features/explore/Logs/Logs.tsx +++ b/public/app/features/explore/Logs/Logs.tsx @@ -52,6 +52,7 @@ import { ControlledLogRows } from 'app/features/logs/components/ControlledLogRow import { InfiniteScroll } from 'app/features/logs/components/InfiniteScroll'; import { LogRows } from 'app/features/logs/components/LogRows'; import { LogRowContextModal } from 'app/features/logs/components/log-context/LogRowContextModal'; +import { LogLineContext } from 'app/features/logs/components/panel/LogLineContext'; import { LogList, LogListControlOptions } from 'app/features/logs/components/panel/LogList'; import { isDedupStrategy, isLogsSortOrder } from 'app/features/logs/components/panel/LogListContext'; import { LogLevelColor, dedupLogRows } from 'app/features/logs/logsModel'; @@ -767,7 +768,7 @@ const UnthemedLogs: React.FunctionComponent = (props: Props) => { return ( <> - {getRowContext && contextRow && ( + {(!config.featureToggles.newLogsPanel || !config.featureToggles.newLogContext) && getRowContext && contextRow && ( = (props: Props) => { timeZone={timeZone} /> )} + {config.featureToggles.newLogsPanel && config.featureToggles.newLogContext && getRowContext && contextRow && ( + getRowContext(row, contextRow, options)} + getRowContextQuery={getRowContextQuery} + getLogRowContextUi={getLogRowContextUi} + logOptionsStorageKey={SETTING_KEY_ROOT} + timeZone={timeZone} + displayedFields={displayedFields} + onClickShowField={showField} + onClickHideField={hideField} + /> + )} { +const useResourceAttributesExtensionLinks = ( + process: TraceProcess, + spanTags: TraceKeyValuePair[], + datasourceType: string, + datasourceUid: string +) => { // Stable context for useMemo inside usePluginLinks const context: PluginExtensionResourceAttributesContext = useMemo(() => { const attributes = (process.tags ?? []).reduce>((acc, tag) => { @@ -63,14 +68,24 @@ const useResourceAttributesExtensionLinks = (process: TraceProcess, datasourceTy return acc; }, {}); + const spanAttributes = (spanTags ?? []).reduce>((acc, tag) => { + if (acc[tag.key]) { + acc[tag.key].push(tag.value); + } else { + acc[tag.key] = [tag.value]; + } + return acc; + }, {}); + return { attributes, + spanAttributes, datasource: { type: datasourceType, uid: datasourceUid, }, }; - }, [process.tags, datasourceType, datasourceUid]); + }, [process.tags, spanTags, datasourceType, datasourceUid]); const { links } = usePluginLinks({ extensionPointId: PluginExtensionPoints.TraceViewResourceAttributes, @@ -328,7 +343,7 @@ export default function SpanDetail(props: SpanDetailProps) { }); const focusSpanLink = createFocusSpanLink(traceID, spanID); - const resourceLinksGetter = useResourceAttributesExtensionLinks(process, datasourceType, datasourceUid); + const resourceLinksGetter = useResourceAttributesExtensionLinks(process, tags, datasourceType, datasourceUid); return (
@@ -348,6 +363,7 @@ export default function SpanDetail(props: SpanDetailProps) { data={tags} label={t('explore.span-detail.label-span-attributes', 'Span attributes')} isOpen={isTagsOpen} + linksGetter={resourceLinksGetter} onToggle={() => tagsToggle(spanID)} /> {process.tags && ( diff --git a/public/app/features/explore/TraceView/components/utils/date.test.ts b/public/app/features/explore/TraceView/components/utils/date.test.ts index 2be14600ebc..f18daf51259 100644 --- a/public/app/features/explore/TraceView/components/utils/date.test.ts +++ b/public/app/features/explore/TraceView/components/utils/date.test.ts @@ -58,4 +58,9 @@ describe('formatDuration', () => { const input = 0; expect(formatDuration(input)).toBe('0μs'); }); + + it('skips secondary units that are a whole multiple of the primary unit', () => { + const input = 299898037.75; + expect(formatDuration(input)).toBe('5m'); + }); }); diff --git a/public/app/features/explore/TraceView/components/utils/date.tsx b/public/app/features/explore/TraceView/components/utils/date.tsx index 3c358885359..22b4b5ab9ee 100644 --- a/public/app/features/explore/TraceView/components/utils/date.tsx +++ b/public/app/features/explore/TraceView/components/utils/date.tsx @@ -95,9 +95,24 @@ export function formatDuration(duration: number): string { return `${_round(duration / primaryUnit.microseconds, 2)}${primaryUnit.unit}`; } - const primaryValue = Math.floor(duration / primaryUnit.microseconds); + let primaryValue = Math.floor(duration / primaryUnit.microseconds); + let secondaryValue = (duration / secondaryUnit.microseconds) % primaryUnit.ofPrevious; + const secondaryValueRounded = Math.round(secondaryValue); + + // Handle rollover case before rounding (e.g., 60s should become 1m, not 0m 60s) + if (secondaryValueRounded === primaryUnit.ofPrevious) { + primaryValue += 1; + secondaryValue = 0; + } else { + secondaryValue = secondaryValueRounded; + } + const primaryUnitString = `${primaryValue}${primaryUnit.unit}`; - const secondaryValue = Math.round((duration / secondaryUnit.microseconds) % primaryUnit.ofPrevious); + + if (secondaryValue === 0) { + return primaryUnitString; + } + const secondaryUnitString = `${secondaryValue}${secondaryUnit.unit}`; - return secondaryValue === 0 ? primaryUnitString : `${primaryUnitString} ${secondaryUnitString}`; + return `${primaryUnitString} ${secondaryUnitString}`; } diff --git a/public/app/features/logs/components/InfiniteScroll.tsx b/public/app/features/logs/components/InfiniteScroll.tsx index 83f8c7fdab0..90f0919ca7d 100644 --- a/public/app/features/logs/components/InfiniteScroll.tsx +++ b/public/app/features/logs/components/InfiniteScroll.tsx @@ -310,7 +310,7 @@ function getNextRange(visibleRange: AbsoluteTimeRange, currentRange: TimeRange, export const SCROLLING_THRESHOLD = 1e3; // To get more logs, the difference between the visible range and the current range should be 1 second or more. -function canScrollTop( +export function canScrollTop( visibleRange: AbsoluteTimeRange, currentRange: TimeRange, timeZone: TimeZone, diff --git a/public/app/features/logs/components/LogRowMenuCell.tsx b/public/app/features/logs/components/LogRowMenuCell.tsx index 2e3699733fd..892a5eea68b 100644 --- a/public/app/features/logs/components/LogRowMenuCell.tsx +++ b/public/app/features/logs/components/LogRowMenuCell.tsx @@ -194,7 +194,6 @@ function addClickListenersToNode(nodes: ReactNode[], row: LogRowModel) { return node; } return cloneElement(node, { - // @ts-expect-error onClick: (event: MouseEvent) => { onClick(event, row); }, diff --git a/public/app/features/logs/components/panel/InfiniteScroll.tsx b/public/app/features/logs/components/panel/InfiniteScroll.tsx index c6c310ec88c..c696f2670b3 100644 --- a/public/app/features/logs/components/panel/InfiniteScroll.tsx +++ b/public/app/features/logs/components/panel/InfiniteScroll.tsx @@ -7,7 +7,7 @@ import { t } from '@grafana/i18n'; import { config, reportInteraction } from '@grafana/runtime'; import { Spinner, useStyles2 } from '@grafana/ui'; -import { canScrollBottom, getVisibleRange, ScrollDirection, shouldLoadMore } from '../InfiniteScroll'; +import { canScrollBottom, canScrollTop, getVisibleRange, ScrollDirection, shouldLoadMore } from '../InfiniteScroll'; import { getStyles, LogLine } from './LogLine'; import { LogLineMessage } from './LogLineMessage'; @@ -25,7 +25,8 @@ interface Props { children: (props: ChildrenProps) => ReactNode; displayedFields: string[]; handleOverflow: (index: number, id: string, height?: number) => void; - loadMore?: (range: AbsoluteTimeRange) => void; + infiniteScrollMode: InfiniteScrollMode; + loadMore?: LoadMoreLogsType; logs: LogListModel[]; onClick: (e: MouseEvent, log: LogListModel) => void; scrollElement: HTMLDivElement | null; @@ -38,12 +39,17 @@ interface Props { wrapLogMessage: boolean; } -type InfiniteLoaderState = 'idle' | 'out-of-bounds' | 'pre-scroll' | 'loading'; +type InfiniteLoaderState = 'idle' | 'out-of-bounds' | 'pre-scroll-top' | 'pre-scroll-bottom' | 'loading'; +export type InfiniteScrollMode = 'interval' | 'unlimited'; +export type LoadMoreLogsType = + | ((range: AbsoluteTimeRange) => void) + | ((range: AbsoluteTimeRange, scrollDirection: ScrollDirection) => void); export const InfiniteScroll = ({ children, displayedFields, handleOverflow, + infiniteScrollMode, loadMore, logs, onClick, @@ -65,6 +71,7 @@ export const InfiniteScroll = ({ const countRef = useRef(0); const lastLogOfPage = useRef([]); const styles = useStyles2(getStyles, virtualization); + const resetStateTimeout = useRef | null>(null); useEffect(() => { // Logs have not changed, ignore effect @@ -74,12 +81,14 @@ export const InfiniteScroll = ({ // New logs are from infinite scrolling if (infiniteLoaderState === 'loading') { // out-of-bounds if no new logs returned - setInfiniteLoaderState(logs.length === prevLogs.length ? 'out-of-bounds' : 'idle'); + setInfiniteLoaderState( + logs.length === prevLogs.length && infiniteScrollMode === 'interval' ? 'out-of-bounds' : 'idle' + ); } else { lastLogOfPage.current = []; setAutoScroll(true); } - }, [infiniteLoaderState, logs, prevLogs]); + }, [infiniteLoaderState, infiniteScrollMode, logs, prevLogs]); useEffect(() => { if (prevSortOrder && prevSortOrder !== sortOrder) { @@ -94,21 +103,31 @@ export const InfiniteScroll = ({ } }, [autoScroll, setInitialScrollPosition]); - const onLoadMore = useCallback(() => { - const newRange = canScrollBottom(getVisibleRange(logs), timeRange, timeZone, sortOrder); - if (!newRange) { - setInfiniteLoaderState('out-of-bounds'); - return; - } - lastLogOfPage.current.push(logs[logs.length - 1].uid); - setInfiniteLoaderState('loading'); - loadMore?.(newRange); + const onLoadMore = useCallback( + (scrollDirection: ScrollDirection) => { + const newRange = + scrollDirection === ScrollDirection.Bottom + ? canScrollBottom(getVisibleRange(logs), timeRange, timeZone, sortOrder) + : canScrollTop(getVisibleRange(logs), timeRange, timeZone, sortOrder); + if (!newRange && infiniteScrollMode === 'interval') { + setInfiniteLoaderState('out-of-bounds'); + return; + } + if (scrollDirection === ScrollDirection.Bottom) { + lastLogOfPage.current.push(logs[logs.length - 1].uid); + } else { + lastLogOfPage.current.push(logs[0].uid); + } + setInfiniteLoaderState('loading'); + loadMore?.(newRange ?? getVisibleRange(logs), scrollDirection); - reportInteraction('grafana_logs_infinite_scrolling', { - direction: 'bottom', - sort_order: sortOrder, - }); - }, [loadMore, logs, sortOrder, timeRange, timeZone]); + reportInteraction('grafana_logs_infinite_scrolling', { + direction: scrollDirection, + sort_order: sortOrder, + }); + }, + [infiniteScrollMode, loadMore, logs, sortOrder, timeRange, timeZone] + ); useEffect(() => { if (!scrollElement || !loadMore || !config.featureToggles.logsInfiniteScrolling) { @@ -116,14 +135,24 @@ export const InfiniteScroll = ({ } function handleScroll(event: Event | WheelEvent) { - if (!scrollElement || !loadMore || !logs.length || infiniteLoaderState !== 'pre-scroll') { + if (!scrollElement || !loadMore || !logs.length) { return; } const scrollDirection = shouldLoadMore(event, lastEvent.current, countRef, scrollElement, lastScroll.current); lastEvent.current = event; lastScroll.current = scrollElement.scrollTop; - if (scrollDirection === ScrollDirection.Bottom) { - onLoadMore(); + if (infiniteLoaderState !== 'pre-scroll-bottom' && infiniteLoaderState !== 'pre-scroll-top') { + if (infiniteScrollMode === 'unlimited' && scrollDirection === ScrollDirection.Top) { + setInfiniteLoaderState('pre-scroll-top'); + resetStateTimeout.current = setTimeout(() => { + setInfiniteLoaderState((state) => (state === 'pre-scroll-top' ? 'idle' : state)); + }, 10000); + return; + } + return; + } + if (scrollDirection !== ScrollDirection.NoScroll) { + onLoadMore(scrollDirection); } } @@ -134,7 +163,26 @@ export const InfiniteScroll = ({ scrollElement.removeEventListener('scroll', handleScroll); scrollElement.removeEventListener('wheel', handleScroll); }; - }, [infiniteLoaderState, loadMore, logs.length, onLoadMore, scrollElement]); + }, [infiniteLoaderState, infiniteScrollMode, loadMore, logs.length, onLoadMore, scrollElement]); + + useEffect(() => { + return () => { + if (resetStateTimeout.current) { + clearTimeout(resetStateTimeout.current); + } + }; + }, []); + + const loadMoreTop = useCallback(() => { + if (resetStateTimeout.current) { + clearTimeout(resetStateTimeout.current); + } + onLoadMore(ScrollDirection.Top); + }, [onLoadMore]); + + const loadMoreBottom = useCallback(() => { + onLoadMore(ScrollDirection.Bottom); + }, [onLoadMore]); const Renderer = useCallback( ({ index, style }: ListChildComponentProps) => { @@ -143,7 +191,7 @@ export const InfiniteScroll = ({ {getMessageFromInfiniteLoaderState(infiniteLoaderState, sortOrder)} @@ -170,9 +218,9 @@ export const InfiniteScroll = ({ displayedFields, handleOverflow, infiniteLoaderState, + loadMoreBottom, logs, onClick, - onLoadMore, showTime, sortOrder, styles, @@ -192,7 +240,7 @@ export const InfiniteScroll = ({ const lastLogIndex = logs.length - 1; const preScrollIndex = logs.length - 2; if (props.visibleStopIndex >= lastLogIndex) { - setInfiniteLoaderState('pre-scroll'); + setInfiniteLoaderState('pre-scroll-bottom'); } else if (props.visibleStartIndex < preScrollIndex) { setInfiniteLoaderState('idle'); } @@ -204,7 +252,18 @@ export const InfiniteScroll = ({ const itemCount = logs.length && loadMore && infiniteLoaderState !== 'idle' ? logs.length + 1 : logs.length; - return <>{children({ getItemKey, itemCount, onItemsRendered, Renderer })}; + return ( + <> + {infiniteLoaderState === 'pre-scroll-top' && ( +
+ + {t('logs.infinite-scroll.load-more', 'Scroll to load more')} + +
+ )} + {children({ getItemKey, itemCount, onItemsRendered, Renderer })} + + ); }; function getMessageFromInfiniteLoaderState(state: InfiniteLoaderState, order: LogsSortOrder) { @@ -220,7 +279,7 @@ function getMessageFromInfiniteLoaderState(state: InfiniteLoaderState, order: Lo ); - case 'pre-scroll': + case 'pre-scroll-bottom': return t('logs.infinite-scroll.load-more', 'Scroll to load more'); default: return null; diff --git a/public/app/features/logs/components/panel/LogLine.tsx b/public/app/features/logs/components/panel/LogLine.tsx index 4f099dac59a..f3b994250f5 100644 --- a/public/app/features/logs/components/panel/LogLine.tsx +++ b/public/app/features/logs/components/panel/LogLine.tsx @@ -524,6 +524,14 @@ export const getStyles = (theme: GrafanaTheme2, virtualization?: LogLineVirtuali border: 'none', display: 'inline', }), + loadMoreTopContainer: css({ + backgroundColor: tinycolor(theme.colors.background.primary).setAlpha(0.75).toString(), + left: 0, + position: 'absolute', + top: 0, + width: '100%', + zIndex: theme.zIndex.navbarFixed, + }), overflows: css({ outline: 'solid 1px red', }), diff --git a/public/app/features/logs/components/panel/LogLineContext.test.tsx b/public/app/features/logs/components/panel/LogLineContext.test.tsx new file mode 100644 index 00000000000..31f77df4f3e --- /dev/null +++ b/public/app/features/logs/components/panel/LogLineContext.test.tsx @@ -0,0 +1,545 @@ +import { render, screen, waitFor, userEvent } from 'test/test-utils'; + +import { + createDataFrame, + FieldType, + LogRowContextQueryDirection, + LogsSortOrder, + SplitOpenOptions, +} from '@grafana/data'; + +import { dataFrameToLogsModel } from '../../logsModel'; + +import { LogLineContext } from './LogLineContext'; + +jest.mock('@grafana/assistant', () => ({ + ...jest.requireActual('@grafana/assistant'), + useAssistant: jest.fn(() => [true, jest.fn()]), +})); + +const dfBefore = createDataFrame({ + fields: [ + { + name: 'time', + type: FieldType.time, + values: ['2019-04-26T07:28:11.352440161Z', '2019-04-26T09:28:11.352440161Z'], + }, + { + name: 'message', + type: FieldType.string, + values: ['foo123', 'foo123'], + }, + ], +}); +const dfNow = createDataFrame({ + fields: [ + { + name: 'time', + type: FieldType.time, + values: ['2019-04-26T09:28:11.352440161Z'], + }, + { + name: 'message', + type: FieldType.string, + values: ['foo123'], + }, + ], +}); +const dfAfter = createDataFrame({ + fields: [ + { + name: 'time', + type: FieldType.time, + values: ['2019-04-26T14:42:50.991981292Z', '2019-04-26T16:28:11.352440161Z'], + }, + { + name: 'message', + type: FieldType.string, + values: ['foo123', 'bar123'], + }, + ], +}); + +let getRowContext = jest.fn(); +const dispatchMock = jest.fn(); +jest.mock('app/types/store', () => ({ + ...jest.requireActual('app/types/store'), + useDispatch: () => dispatchMock, +})); + +const splitOpenSym = Symbol('splitOpen'); +const splitOpen = jest.fn().mockReturnValue(splitOpenSym); +jest.mock('app/features/explore/state/main', () => ({ + ...jest.requireActual('app/features/explore/state/main'), + splitOpen: (arg?: SplitOpenOptions) => { + return splitOpen(arg); + }, +})); + +const logs = dataFrameToLogsModel([dfNow]); +const row = logs.rows[0]; + +const timeZone = 'UTC'; + +describe('LogLineContext', () => { + let uniqueRefIdCounter = 1; + + beforeEach(() => { + uniqueRefIdCounter = 1; + getRowContext = jest.fn().mockImplementation(async (_, options) => { + uniqueRefIdCounter += 1; + const refId = `refid_${uniqueRefIdCounter}`; + if (options.direction === LogRowContextQueryDirection.Forward) { + return { + data: [ + { + refId, + ...dfBefore, + }, + ], + }; + } else { + return { + data: [ + { + refId, + ...dfAfter, + }, + ], + }; + } + }); + }); + + test('Should not render when it is closed', async () => { + render( + {}} + getRowContext={getRowContext} + timeZone={timeZone} + sortOrder={LogsSortOrder.Descending} + /> + ); + + await waitFor(() => expect(screen.queryByText('Log context')).not.toBeInTheDocument()); + }); + + test('Should render when it is open', async () => { + render( + {}} + getRowContext={getRowContext} + timeZone={timeZone} + sortOrder={LogsSortOrder.Descending} + /> + ); + + await waitFor(() => expect(screen.queryByText('Log context')).toBeInTheDocument()); + }); + + test('Should call not getRowContext when closed', async () => { + render( + {}} + getRowContext={getRowContext} + timeZone={timeZone} + sortOrder={LogsSortOrder.Descending} + /> + ); + + await waitFor(() => expect(getRowContext).not.toHaveBeenCalled()); + }); + + test('Should call getRowContext on open', async () => { + render( + {}} + getRowContext={getRowContext} + timeZone={timeZone} + sortOrder={LogsSortOrder.Descending} + /> + ); + await waitFor(() => expect(getRowContext).toHaveBeenCalledTimes(2)); + }); + + test('should render 3 lines containing `foo123`', async () => { + render( + {}} + getRowContext={getRowContext} + timeZone={timeZone} + sortOrder={LogsSortOrder.Descending} + /> + ); + // 1 in before, 1 in current, 1 in after + await waitFor(() => expect(screen.getAllByText('foo123').length).toBe(3)); + }); + + test('should render 3 lines containing `foo123` with the same ms timestamp', async () => { + const dfBeforeNs = createDataFrame({ + fields: [ + { + name: 'time', + type: FieldType.time, + values: [1, 1], + }, + { + name: 'message', + type: FieldType.string, + values: ['foo123', 'foo123'], + }, + { + name: 'tsNs', + type: FieldType.string, + values: ['1', '2'], + }, + ], + }); + const dfNowNs = createDataFrame({ + fields: [ + { + name: 'time', + type: FieldType.time, + values: [1], + }, + { + name: 'message', + type: FieldType.string, + values: ['foo123'], + }, + { + name: 'tsNs', + type: FieldType.string, + values: ['2'], + }, + ], + }); + const dfAfterNs = createDataFrame({ + fields: [ + { + name: 'time', + type: FieldType.time, + values: [1, 1], + }, + { + name: 'message', + type: FieldType.string, + values: ['foo123', 'foo123'], + }, + { + name: 'tsNs', + type: FieldType.string, + values: ['2', '3'], + }, + ], + }); + + let uniqueRefIdCounter = 1; + const logs = dataFrameToLogsModel([dfNowNs]); + const row = logs.rows[0]; + const getRowContext = jest.fn().mockImplementation(async (_, options) => { + uniqueRefIdCounter += 1; + const refId = `refid_${uniqueRefIdCounter}`; + if (uniqueRefIdCounter === 2) { + return { + data: [ + { + refId, + ...dfBeforeNs, + }, + ], + }; + } else if (uniqueRefIdCounter === 3) { + return { + data: [ + { + refId, + ...dfAfterNs, + }, + ], + }; + } + return { data: [] }; + }); + + render( + {}} + getRowContext={getRowContext} + timeZone={timeZone} + sortOrder={LogsSortOrder.Descending} + /> + ); + + // 1 in before, 1 in current, 1 in after + await waitFor(() => { + expect(screen.getAllByText('foo123').length).toBe(3); + }); + }); + + test('Should highlight the same `foo123` searchwords', async () => { + const dfBeforeNs = createDataFrame({ + fields: [ + { + name: 'time', + type: FieldType.time, + values: [1, 1], + }, + { + name: 'message', + type: FieldType.string, + values: ['this contains foo123', 'this contains foo123'], + }, + { + name: 'tsNs', + type: FieldType.string, + values: ['1', '2'], + }, + ], + }); + const dfNowNs = createDataFrame({ + fields: [ + { + name: 'time', + type: FieldType.time, + values: [1], + }, + { + name: 'message', + type: FieldType.string, + values: ['this contains foo123'], + }, + { + name: 'tsNs', + type: FieldType.string, + values: ['2'], + }, + ], + }); + const dfAfterNs = createDataFrame({ + fields: [ + { + name: 'time', + type: FieldType.time, + values: [1, 1], + }, + { + name: 'message', + type: FieldType.string, + values: ['this contains foo123', 'this contains foo123'], + }, + { + name: 'tsNs', + type: FieldType.string, + values: ['2', '3'], + }, + ], + }); + + let uniqueRefIdCounter = 1; + const logs = dataFrameToLogsModel([dfNowNs]); + const row = logs.rows[0]; + row.searchWords = ['foo123']; + const getRowContext = jest.fn().mockImplementation(async (_, options) => { + uniqueRefIdCounter += 1; + const refId = `refid_${uniqueRefIdCounter}`; + if (uniqueRefIdCounter === 2) { + return { + data: [ + { + refId, + ...dfBeforeNs, + }, + ], + }; + } else if (uniqueRefIdCounter === 3) { + return { + data: [ + { + refId, + ...dfAfterNs, + }, + ], + }; + } + return { data: [] }; + }); + + render( + {}} + getRowContext={getRowContext} + timeZone={timeZone} + sortOrder={LogsSortOrder.Descending} + /> + ); + + // there need to be 3 lines with that message, all `foo123` should be highlighted + await waitFor(() => { + expect(screen.getAllByText('foo123')).toHaveLength(3); + expect(screen.getAllByText('this contains')).toHaveLength(3); + }); + }); + + test('Should show a split view button', async () => { + const getRowContextQuery = jest.fn().mockResolvedValue({ datasource: { uid: 'test-uid' } }); + + render( + {}} + getRowContext={getRowContext} + getRowContextQuery={getRowContextQuery} + timeZone={timeZone} + sortOrder={LogsSortOrder.Descending} + /> + ); + + await waitFor(() => + expect( + screen.getByRole('button', { + name: /open in split view/i, + }) + ).toBeInTheDocument() + ); + }); + + test('Should not show a split view button', async () => { + render( + {}} + getRowContext={getRowContext} + timeZone={timeZone} + sortOrder={LogsSortOrder.Descending} + /> + ); + + await waitFor(() => { + expect( + screen.queryByRole('button', { + name: /open in split view/i, + }) + ).not.toBeInTheDocument(); + }); + }); + + test('Should call getRowContextQuery', async () => { + const getRowContextQuery = jest.fn().mockResolvedValue({ datasource: { uid: 'test-uid' } }); + render( + {}} + getRowContext={getRowContext} + getRowContextQuery={getRowContextQuery} + timeZone={timeZone} + sortOrder={LogsSortOrder.Descending} + /> + ); + + await waitFor(() => expect(getRowContextQuery).toHaveBeenCalledTimes(1)); + }); + + test('Should close modal', async () => { + const getRowContextQuery = jest.fn().mockResolvedValue({ datasource: { uid: 'test-uid' } }); + const onClose = jest.fn(); + render( + + ); + + const splitViewButton = await screen.findByRole('button', { + name: /open in split view/i, + }); + + await userEvent.click(splitViewButton); + + await waitFor(() => expect(onClose).toHaveBeenCalled()); + }); + + test('Should create correct splitOpen', async () => { + const queryObj = { datasource: { uid: 'test-uid' } }; + const getRowContextQuery = jest.fn().mockResolvedValue(queryObj); + const onClose = jest.fn(); + + render( + + ); + + const splitViewButton = await screen.findByRole('button', { + name: /open in split view/i, + }); + + await userEvent.click(splitViewButton); + + await waitFor(() => + expect(splitOpen).toHaveBeenCalledWith( + expect.objectContaining({ + queries: [queryObj], + panelsState: { + logs: { + id: row.uid, + }, + }, + }) + ) + ); + }); + + test('Should dispatch splitOpen', async () => { + const getRowContextQuery = jest.fn().mockResolvedValue({ datasource: { uid: 'test-uid' } }); + const onClose = jest.fn(); + + render( + + ); + + const splitViewButton = await screen.findByRole('button', { + name: /open in split view/i, + }); + + await userEvent.click(splitViewButton); + + await waitFor(() => expect(dispatchMock).toHaveBeenCalledWith(splitOpenSym)); + }); +}); diff --git a/public/app/features/logs/components/panel/LogLineContext.tsx b/public/app/features/logs/components/panel/LogLineContext.tsx new file mode 100644 index 00000000000..db28cdc0fdb --- /dev/null +++ b/public/app/features/logs/components/panel/LogLineContext.tsx @@ -0,0 +1,447 @@ +import { css } from '@emotion/css'; +import { partition } from 'lodash'; +import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; + +import { + DataQueryResponse, + DataSourceWithLogsContextSupport, + GrafanaTheme2, + LogRowContextOptions, + LogRowContextQueryDirection, + LogsDedupStrategy, + LogsSortOrder, + dateTime, + TimeRange, + LoadingState, + CoreApp, + LogRowModel, + AbsoluteTimeRange, + EventBusSrv, + store, +} from '@grafana/data'; +import { Trans, t } from '@grafana/i18n'; +import { config, reportInteraction } from '@grafana/runtime'; +import { DataQuery, TimeZone } from '@grafana/schema'; +import { Button, Collapse, Modal, useTheme2 } from '@grafana/ui'; +import { splitOpen } from 'app/features/explore/state/main'; +import { useDispatch } from 'app/types/store'; + +import { dataFrameToLogsModel } from '../../logsModel'; +import { sortLogRows } from '../../utils'; +import { ScrollDirection } from '../InfiniteScroll'; +import { LoadingIndicator } from '../LoadingIndicator'; + +import { LogLineDetailsLog } from './LogLineDetailsLog'; +import { LogList } from './LogList'; +import { LogListModel } from './processing'; +import { ScrollToLogsEvent } from './virtualization'; + +interface LogLineContextProps { + log: LogRowModel | LogListModel; + logOptionsStorageKey?: string; + open: boolean; + timeZone: TimeZone; + onClose: () => void; + getRowContext: (row: LogRowModel, options: LogRowContextOptions) => Promise; + getRowContextQuery?: ( + row: LogRowModel, + options?: LogRowContextOptions, + cacheFilters?: boolean + ) => Promise; + sortOrder?: LogsSortOrder; + runContextQuery?: () => void; + getLogRowContextUi?: DataSourceWithLogsContextSupport['getLogRowContextUi']; + displayedFields?: string[]; + onClickShowField?: (key: string) => void; + onClickHideField?: (key: string) => void; +} + +const PAGE_SIZE = 100; + +export const LogLineContext = memo( + ({ + log, + logOptionsStorageKey, + open, + sortOrder = logOptionsStorageKey + ? (store.get(`${logOptionsStorageKey}.sortOrder`) ?? LogsSortOrder.Descending) + : LogsSortOrder.Descending, + timeZone, + getLogRowContextUi, + getRowContextQuery, + onClose, + getRowContext, + displayedFields = [], + onClickShowField, + onClickHideField, + }: LogLineContextProps) => { + const containerRef = useRef(null); + const [contextQuery, setContextQuery] = useState(null); + const [aboveLogs, setAboveLogs] = useState([]); + const [belowLogs, setBelowLogs] = useState([]); + const [initialized, setInitialized] = useState(false); + const allLogs = useMemo(() => [...aboveLogs, log, ...belowLogs], [log, belowLogs, aboveLogs]); + const [aboveState, setAboveState] = useState(LoadingState.NotStarted); + const [belowState, setBelowState] = useState(LoadingState.NotStarted); + const [showLog, setShowLog] = useState(false); + const eventBusRef = useRef(new EventBusSrv()); + + const dispatch = useDispatch(); + const theme = useTheme2(); + const styles = getStyles(theme); + + const timeRange = useMemo(() => { + const fromMs = + sortOrder === LogsSortOrder.Ascending ? allLogs[0].timeEpochMs : allLogs[allLogs.length - 1].timeEpochMs; + let toMs = + sortOrder === LogsSortOrder.Ascending ? allLogs[allLogs.length - 1].timeEpochMs : allLogs[0].timeEpochMs; + // In case we have a lot of logs and from and to have same millisecond + // we add 1 millisecond to toMs to make sure we have a range + if (fromMs === toMs) { + toMs += 1; + } + const from = dateTime(fromMs); + const to = dateTime(toMs); + + const range: TimeRange = { + from, + to, + raw: { + from, + to, + }, + }; + return range; + }, [allLogs, sortOrder]); + + const updateContextQuery = useCallback(async () => { + const contextQuery = getRowContextQuery ? await getRowContextQuery(log) : null; + setContextQuery(contextQuery); + }, [log, getRowContextQuery]); + + const updateResults = useCallback(async () => { + setAboveLogs([]); + setBelowLogs([]); + await updateContextQuery(); + setInitialized(false); + }, [updateContextQuery]); + + useEffect(() => { + if (open) { + updateContextQuery(); + } + }, [updateContextQuery, open]); + + const getContextLogs = useCallback( + async (place: 'above' | 'below', refLog: LogRowModel): Promise => { + const result = await getRowContext(normalizeLogRefId(refLog), { + limit: PAGE_SIZE, + direction: getLoadMoreDirection(place, sortOrder), + }); + + const newLogs = dataFrameToLogsModel(result.data).rows; + if (sortOrder === LogsSortOrder.Ascending) { + newLogs.reverse(); + } + return newLogs.filter((r) => !containsRow(allLogs, r)); + }, + [allLogs, getRowContext, sortOrder] + ); + + const loadMore = useCallback( + async (place: 'above' | 'below', refLog: LogRowModel) => { + const setState = place === 'above' ? setAboveState : setBelowState; + setState(LoadingState.Loading); + + try { + const newLogs = (await getContextLogs(place, refLog)).map((r) => + // apply the original row's searchWords to all the rows for highlighting + !r.searchWords || !r.searchWords?.length ? { ...r, searchWords: log.searchWords } : r + ); + const [older, newer] = partition(newLogs, (newRow) => newRow.timeEpochNs > log.timeEpochNs); + const newAbove = sortOrder === LogsSortOrder.Ascending ? newer : older; + const newBelow = sortOrder === LogsSortOrder.Ascending ? older : newer; + + setAboveLogs((aboveLogs: LogRowModel[]) => { + return newAbove.length > 0 ? sortLogRows([...newAbove, ...aboveLogs], sortOrder) : aboveLogs; + }); + setBelowLogs((belowLogs: LogRowModel[]) => { + return newBelow.length > 0 ? sortLogRows([...belowLogs, ...newBelow], sortOrder) : belowLogs; + }); + + setState(LoadingState.NotStarted); + if (!newAbove.length && place === 'above') { + setAboveState(LoadingState.Done); + } + if (!newBelow.length && place === 'below') { + setBelowState(LoadingState.Done); + } + } catch { + setState(LoadingState.Error); + } + }, + [getContextLogs, log, sortOrder] + ); + + useEffect(() => { + if (!open) { + return; + } + if (!initialized) { + Promise.all([loadMore('above', log), loadMore('below', log)]).then(() => {}); + setInitialized(true); + } + }, [initialized, loadMore, log, open]); + + const handleLoadMore = useCallback( + (_: AbsoluteTimeRange, direction: ScrollDirection) => { + if (direction === ScrollDirection.Bottom) { + loadMore('below', allLogs[allLogs.length - 1]); + } else { + loadMore('above', allLogs[0]); + } + }, + [allLogs, loadMore] + ); + + const onScrollCenterClick = useCallback(() => { + eventBusRef.current.publish( + new ScrollToLogsEvent({ + scrollTo: log.uid, + }) + ); + }, [log.uid]); + + const wrapLogMessage = logOptionsStorageKey ? store.getBool(`${logOptionsStorageKey}.wrapLogMessage`, true) : true; + const syntaxHighlighting = logOptionsStorageKey + ? store.getBool(`${logOptionsStorageKey}.syntaxHighlighting`, true) + : true; + // @todo: Remove when the LogRows are deprecated + const logListModel = useMemo( + () => + log instanceof LogListModel + ? log + : new LogListModel(log, { + escape: false, + timeZone, + wrapLogMessage, + }), + [log, timeZone, wrapLogMessage] + ); + + return ( + + {config.featureToggles.logsContextDatasourceUi && getLogRowContextUi && ( +
{getLogRowContextUi(log, updateResults)}
+ )} + setShowLog(!showLog)} + className={styles.referenceLogLine} + label={t('logs.log-line-context.title-log-line', 'Referenced log line')} + > + + +
+ {aboveState === LoadingState.Loading && ( + + )} + {aboveState === LoadingState.Done && ( + No more logs available. + )} +
+
+
+ {containerRef.current && ( + + )} +
+
+
+ {belowState === LoadingState.Loading && ( + + )} + {belowState === LoadingState.Done && ( + No more logs available. + )} +
+ + + + {contextQuery?.datasource?.uid && ( + + )} + +
+ ); + } +); +LogLineContext.displayName = 'LogLineContext'; + +const getStyles = (theme: GrafanaTheme2) => { + return { + modal: css({ + width: '85vw', + height: '80%', + [theme.breakpoints.down('md')]: { + width: '100%', + minHeight: '100%', + }, + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + }), + datasourceUi: css({ + display: 'flex', + alignItems: 'center', + }), + loadingIndicator: css({ + height: theme.spacing(3), + minHeight: theme.spacing(3), + textAlign: 'center', + }), + referenceLogLine: css({ + flex: 0, + }), + wrapper: css({ + border: `1px solid ${theme.colors.border.weak}`, + padding: theme.spacing(0, 1, 1, 0), + flex: 1, + height: '100%', + }), + logsContainer: css({ + height: '100%', + overflow: 'hidden', + }), + flexColumn: css({ + display: 'flex', + flexDirection: 'column', + padding: theme.spacing(0, 3, 3, 3), + height: '100%', + }), + link: css({ + color: theme.colors.text.secondary, + fontSize: theme.typography.bodySmall.fontSize, + ':hover': { + color: theme.colors.text.link, + }, + }), + logPreview: css({ + overflow: 'hidden', + textAlign: 'left', + textOverflow: 'ellipsis', + width: '75vw', + whiteSpace: 'nowrap', + }), + }; +}; + +const getLoadMoreDirection = (place: 'above' | 'below', sortOrder: LogsSortOrder): LogRowContextQueryDirection => { + if (place === 'above' && sortOrder === LogsSortOrder.Descending) { + return LogRowContextQueryDirection.Forward; + } + if (place === 'below' && sortOrder === LogsSortOrder.Ascending) { + return LogRowContextQueryDirection.Forward; + } + + return LogRowContextQueryDirection.Backward; +}; + +const normalizeLogRefId = (log: LogRowModel): LogRowModel => { + // the datasoure plugins often create the context-query based on the row's dataframe's refId, + // by appending something to it. for example: + // - let's say the row's dataframe's refId is "query" + // - the datasource plugin will take "query" and append "-context" to it, so it becomes "query-context". + // - later we want to load even more lines, so we make a context query + // - the datasource plugin does the same transform again, but now the source is "query-context", + // so the new refId becomes "query-context-context" + // - next time it becomes "query-context-context-context", and so on. + // we do not want refIds to grow unbounded. + // to avoid this, we set the refId to a value that does not grow. + // on the other hand, the refId is also used in generating the row's UID, so it is useful + // when the refId is not always the exact same string, otherwise UID duplication can occur, + // which may cause problems. + // so we go with an approach where the refId always changes, but does not grow. + return { + ...log, + dataFrame: { + ...log.dataFrame, + refId: `context_${log.uid ?? log.dataFrame.refId ?? log.timeEpochMs}`, + }, + }; +}; + +const containsRow = (rows: LogRowModel[], row: LogRowModel) => { + return rows.some((r) => r.entry === row.entry && r.timeEpochNs === row.timeEpochNs); +}; diff --git a/public/app/features/logs/components/panel/LogLineDetailsComponent.tsx b/public/app/features/logs/components/panel/LogLineDetailsComponent.tsx index 96429ce4327..fd8490f74cb 100644 --- a/public/app/features/logs/components/panel/LogLineDetailsComponent.tsx +++ b/public/app/features/logs/components/panel/LogLineDetailsComponent.tsx @@ -25,7 +25,8 @@ interface LogLineDetailsComponentProps { } export const LogLineDetailsComponent = memo(({ focusLogLine, log, logs }: LogLineDetailsComponentProps) => { - const { displayedFields, noInteractions, logOptionsStorageKey, setDisplayedFields } = useLogListContext(); + const { displayedFields, noInteractions, logOptionsStorageKey, setDisplayedFields, syntaxHighlighting } = + useLogListContext(); const [search, setSearch] = useState(''); const inputRef = useRef(''); const styles = useStyles2(getStyles); @@ -111,7 +112,7 @@ export const LogLineDetailsComponent = memo(({ focusLogLine, log, logs }: LogLin isOpen={logLineOpen} onToggle={(isOpen: boolean) => handleToggle('logLineOpen', isOpen)} > - + {displayedFields.length > 0 && setDisplayedFields && ( { focusLogLine?.(log); - }, [focusLogLine, log]); + reportInteractionWrapper('logs_log_line_details_header_scroll_to_clicked'); + }, [focusLogLine, log, reportInteractionWrapper]); const copyLogLine = useCallback(() => { copyText(log.entry, containerRef); diff --git a/public/app/features/logs/components/panel/LogLineDetailsLog.tsx b/public/app/features/logs/components/panel/LogLineDetailsLog.tsx index 259d7ca27c5..a4e4b960053 100644 --- a/public/app/features/logs/components/panel/LogLineDetailsLog.tsx +++ b/public/app/features/logs/components/panel/LogLineDetailsLog.tsx @@ -4,15 +4,14 @@ import { memo, useMemo } from 'react'; import { useStyles2 } from '@grafana/ui'; import { getStyles } from './LogLine'; -import { useLogListContext } from './LogListContext'; import { LogListModel } from './processing'; interface Props { log: LogListModel; + syntaxHighlighting: boolean; } -export const LogLineDetailsLog = memo(({ log: originalLog }: Props) => { - const { syntaxHighlighting } = useLogListContext(); +export const LogLineDetailsLog = memo(({ log: originalLog, syntaxHighlighting }: Props) => { const logStyles = useStyles2(getStyles); const log = useMemo(() => { const log = originalLog.clone(); diff --git a/public/app/features/logs/components/panel/LogList.tsx b/public/app/features/logs/components/panel/LogList.tsx index 6350723ff89..df0c8b58fd5 100644 --- a/public/app/features/logs/components/panel/LogList.tsx +++ b/public/app/features/logs/components/panel/LogList.tsx @@ -5,7 +5,6 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, Mou import { Align, VariableSizeList } from 'react-window'; import { - AbsoluteTimeRange, CoreApp, DataFrame, EventBus, @@ -24,7 +23,7 @@ import { ConfirmModal, Icon, PopoverContent, useStyles2, useTheme2 } from '@graf import { PopoverMenu } from 'app/features/explore/Logs/PopoverMenu'; import { GetFieldLinksFn } from 'app/plugins/panel/logs/types'; -import { InfiniteScroll } from './InfiniteScroll'; +import { InfiniteScrollMode, InfiniteScroll, LoadMoreLogsType } from './InfiniteScroll'; import { getGridTemplateColumns } from './LogLine'; import { LogLineDetails, LogLineDetailsMode } from './LogLineDetails'; import { GetRowContextQueryFn, LogLineMenuCustomItem } from './LogLineMenu'; @@ -50,10 +49,11 @@ export interface Props { getFieldLinks?: GetFieldLinksFn; getRowContextQuery?: GetRowContextQueryFn; grammar?: Grammar; + infiniteScrollMode?: InfiniteScrollMode; initialScrollPosition?: 'top' | 'bottom'; isLabelFilterActive?: (key: string, value: string, refId?: string) => Promise; loading?: boolean; - loadMore?: (range: AbsoluteTimeRange) => void; + loadMore?: LoadMoreLogsType; logLineMenuCustomItems?: LogLineMenuCustomItem[]; logOptionsStorageKey?: string; logs: LogRowModel[]; @@ -117,6 +117,7 @@ export const LogList = ({ getFieldLinks, getRowContextQuery, grammar, + infiniteScrollMode, initialScrollPosition = 'top', isLabelFilterActive, loading, @@ -197,6 +198,7 @@ export const LogList = ({ getFieldLinks={getFieldLinks} grammar={grammar} initialScrollPosition={initialScrollPosition} + infiniteScrollMode={infiniteScrollMode} loading={loading} loadMore={loadMore} logs={logs} @@ -215,6 +217,7 @@ const LogListComponent = ({ getFieldLinks, grammar, initialScrollPosition = 'top', + infiniteScrollMode = 'interval', loading, loadMore, logs, @@ -271,6 +274,20 @@ const LogListComponent = ({ useKeyBindings(); const { filterLogs, matchingUids, searchVisible } = useLogListSearchContext(); + const levelFilteredLogs = useMemo( + () => + filterLevels.length === 0 ? processedLogs : processedLogs.filter((log) => filterLevels.includes(log.logLevel)), + [filterLevels, processedLogs] + ); + + const filteredLogs = useMemo( + () => + matchingUids && filterLogs + ? levelFilteredLogs.filter((log) => matchingUids.includes(log.uid)) + : levelFilteredLogs, + [filterLogs, levelFilteredLogs, matchingUids] + ); + const debouncedResetAfterIndex = useMemo(() => { return debounce((index: number) => { listRef.current?.resetAfterIndex(index); @@ -286,10 +303,10 @@ const LogListComponent = ({ useEffect(() => { const subscription = eventBus.subscribe(ScrollToLogsEvent, (e: ScrollToLogsEvent) => - handleScrollToEvent(e, logs.length, listRef.current) + handleScrollToEvent(e, filteredLogs, listRef.current) ); return () => subscription.unsubscribe(); - }, [eventBus, logs.length]); + }, [eventBus, filteredLogs]); useEffect(() => { if (loading) { @@ -379,20 +396,6 @@ const LogListComponent = ({ debouncedResetAfterIndex(0); }, [debouncedResetAfterIndex]); - const levelFilteredLogs = useMemo( - () => - filterLevels.length === 0 ? processedLogs : processedLogs.filter((log) => filterLevels.includes(log.logLevel)), - [filterLevels, processedLogs] - ); - - const filteredLogs = useMemo( - () => - matchingUids && filterLogs - ? levelFilteredLogs.filter((log) => matchingUids.includes(log.uid)) - : levelFilteredLogs, - [filterLogs, levelFilteredLogs, matchingUids] - ); - const focusLogLine = useCallback( (log: LogListModel) => { const index = filteredLogs.indexOf(log); @@ -405,6 +408,15 @@ const LogListComponent = ({ return (
+ {showControls && } + {detailsMode === 'sidebar' && showDetails.length > 0 && ( + + )}
{popoverState.selection && popoverState.selectedRow && (
- {detailsMode === 'sidebar' && showDetails.length > 0 && ( - - )} - {showControls && }
); }; @@ -515,6 +519,7 @@ function getStyles( }), logListContainer: css({ display: 'flex', + flexDirection: 'row-reverse', // Minimum width to prevent rendering issues and a sausage-like logs panel. minWidth: theme.spacing(35), }), @@ -534,10 +539,16 @@ function getStyles( }; } -function handleScrollToEvent(event: ScrollToLogsEvent, logsCount: number, list: VariableSizeList | null) { +function handleScrollToEvent(event: ScrollToLogsEvent, logs: LogListModel[], list: VariableSizeList | null) { if (event.payload.scrollTo === 'top') { list?.scrollTo(0); + } else if (event.payload.scrollTo === 'bottom') { + list?.scrollToItem(logs.length - 1); } else { - list?.scrollToItem(logsCount - 1); + // uid + const index = logs.findIndex((log) => log.uid === event.payload.scrollTo); + if (index >= 0) { + list?.scrollToItem(index, 'center'); + } } } diff --git a/public/app/features/logs/components/panel/virtualization.ts b/public/app/features/logs/components/panel/virtualization.ts index bf84f5fe3ff..6207085d1a5 100644 --- a/public/app/features/logs/components/panel/virtualization.ts +++ b/public/app/features/logs/components/panel/virtualization.ts @@ -370,7 +370,7 @@ export function getScrollbarWidth() { } export interface ScrollToLogsEventPayload { - scrollTo: 'top' | 'bottom'; + scrollTo: 'top' | 'bottom' | string; } export class ScrollToLogsEvent extends BusEventWithPayload { diff --git a/public/app/features/plugins/extensions/usePluginComponents.test.tsx b/public/app/features/plugins/extensions/usePluginComponents.test.tsx index f40955c3f54..255a8a6161c 100644 --- a/public/app/features/plugins/extensions/usePluginComponents.test.tsx +++ b/public/app/features/plugins/extensions/usePluginComponents.test.tsx @@ -1,7 +1,13 @@ import { act, render, renderHook, screen } from '@testing-library/react'; import React from 'react'; -import { PluginContextProvider, PluginExtensionPoints, PluginMeta, PluginType } from '@grafana/data'; +import { + PluginContextProvider, + PluginExtensionPoints, + PluginLoadingStrategy, + PluginMeta, + PluginType, +} from '@grafana/data'; import { config } from '@grafana/runtime'; import { ExtensionRegistriesProvider } from './ExtensionRegistriesContext'; @@ -105,6 +111,32 @@ describe('usePluginComponents()', () => { }, }; + config.apps[pluginId] = { + id: pluginId, + path: '', + version: '', + preload: false, + angular: { + detected: false, + hideDeprecation: false, + }, + loadingStrategy: PluginLoadingStrategy.fetch, + dependencies: { + grafanaVersion: '8.0.0', + plugins: [], + extensions: { + exposedComponents: [], + }, + }, + extensions: { + addedLinks: [], + addedComponents: [], + addedFunctions: [], + exposedComponents: [], + extensionPoints: [], + }, + }; + wrapper = ({ children }: { children: React.ReactNode }) => ( {children} @@ -459,6 +491,49 @@ describe('usePluginComponents()', () => { expect(log.error).not.toHaveBeenCalled(); }); + // It can happen that core Grafana plugins (e.g. traces) reuse core components which implement extension points. + it('should not validate the extension point meta-info for core plugins', () => { + jest.mocked(isGrafanaDevMode).mockReturnValue(true); + + const componentConfig = { + targets: extensionPointId, + title: '1', + description: '1', + component: () =>
Component
, + }; + + // The `AddedComponentsRegistry` is validating if the link is registered in the plugin metadata (config.apps). + config.apps[pluginId].extensions.addedComponents = [componentConfig]; + + wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + + ); + + registries.addedComponentsRegistry.register({ + pluginId, + configs: [componentConfig], + }); + + // Trying to render an extension point that is not defined in the plugin meta + // (No restrictions due to being a core plugin) + let { result } = renderHook(() => usePluginComponents({ extensionPointId }), { wrapper }); + expect(result.current.components.length).toBe(1); + expect(log.error).not.toHaveBeenCalled(); + }); + it('should not validate the extension point id in production mode', () => { // Empty list of extension points in the plugin meta (from plugin.json) wrapper = ({ children }: { children: React.ReactNode }) => ( diff --git a/public/app/features/plugins/extensions/usePluginComponents.tsx b/public/app/features/plugins/extensions/usePluginComponents.tsx index 6f37f3bdbba..d07a2e5ac07 100644 --- a/public/app/features/plugins/extensions/usePluginComponents.tsx +++ b/public/app/features/plugins/extensions/usePluginComponents.tsx @@ -29,6 +29,7 @@ export function usePluginComponents({ return useMemo(() => { const isInsidePlugin = Boolean(pluginContext); + const isCoreGrafanaPlugin = pluginContext?.meta.module.startsWith('core:') ?? false; const components: Array> = []; const extensionsByPlugin: Record = {}; const pluginId = pluginContext?.meta.id ?? ''; @@ -38,7 +39,10 @@ export function usePluginComponents({ }); // Don't show extensions if the extension-point id is invalid in DEV mode - if (isGrafanaDevMode() && !isExtensionPointIdValid({ extensionPointId, pluginId, isInsidePlugin, log: pointLog })) { + if ( + isGrafanaDevMode() && + !isExtensionPointIdValid({ extensionPointId, pluginId, isInsidePlugin, isCoreGrafanaPlugin, log: pointLog }) + ) { return { isLoading: false, components: [], @@ -46,7 +50,12 @@ export function usePluginComponents({ } // Don't show extensions if the extension-point misses meta info (plugin.json) in DEV mode - if (isGrafanaDevMode() && pluginContext && isExtensionPointMetaInfoMissing(extensionPointId, pluginContext)) { + if ( + isGrafanaDevMode() && + !isCoreGrafanaPlugin && + pluginContext && + isExtensionPointMetaInfoMissing(extensionPointId, pluginContext) + ) { pointLog.error(errors.EXTENSION_POINT_META_INFO_MISSING); return { isLoading: false, diff --git a/public/app/features/plugins/extensions/usePluginFunctions.tsx b/public/app/features/plugins/extensions/usePluginFunctions.tsx index 18a8abd7aef..8f8c989e316 100644 --- a/public/app/features/plugins/extensions/usePluginFunctions.tsx +++ b/public/app/features/plugins/extensions/usePluginFunctions.tsx @@ -24,6 +24,7 @@ export function usePluginFunctions({ return useMemo(() => { const isInsidePlugin = Boolean(pluginContext); + const isCoreGrafanaPlugin = pluginContext?.meta.module.startsWith('core:') ?? false; const results: Array> = []; const extensionsByPlugin: Record = {}; const pluginId = pluginContext?.meta.id ?? ''; @@ -32,14 +33,22 @@ export function usePluginFunctions({ extensionPointId, }); - if (isGrafanaDevMode() && !isExtensionPointIdValid({ extensionPointId, pluginId, isInsidePlugin, log: pointLog })) { + if ( + isGrafanaDevMode() && + !isExtensionPointIdValid({ extensionPointId, pluginId, isInsidePlugin, isCoreGrafanaPlugin, log: pointLog }) + ) { return { isLoading: false, functions: [], }; } - if (isGrafanaDevMode() && pluginContext && isExtensionPointMetaInfoMissing(extensionPointId, pluginContext)) { + if ( + isGrafanaDevMode() && + !isCoreGrafanaPlugin && + pluginContext && + isExtensionPointMetaInfoMissing(extensionPointId, pluginContext) + ) { pointLog.error(errors.EXTENSION_POINT_META_INFO_MISSING); return { isLoading: false, diff --git a/public/app/features/plugins/extensions/usePluginLinks.test.tsx b/public/app/features/plugins/extensions/usePluginLinks.test.tsx index b5d984b165c..697c9dfd91f 100644 --- a/public/app/features/plugins/extensions/usePluginLinks.test.tsx +++ b/public/app/features/plugins/extensions/usePluginLinks.test.tsx @@ -1,6 +1,13 @@ import { act, renderHook } from '@testing-library/react'; -import { PluginContextProvider, PluginExtensionPoints, PluginMeta, PluginType } from '@grafana/data'; +import { + PluginContextProvider, + PluginExtensionPoints, + PluginLoadingStrategy, + PluginMeta, + PluginType, +} from '@grafana/data'; +import { config } from '@grafana/runtime'; import { ExtensionRegistriesProvider } from './ExtensionRegistriesContext'; import { log } from './logs/log'; @@ -98,6 +105,32 @@ describe('usePluginLinks()', () => { }, }; + config.apps[pluginId] = { + id: pluginId, + path: '', + version: '', + preload: false, + angular: { + detected: false, + hideDeprecation: false, + }, + loadingStrategy: PluginLoadingStrategy.fetch, + dependencies: { + grafanaVersion: '8.0.0', + plugins: [], + extensions: { + exposedComponents: [], + }, + }, + extensions: { + addedLinks: [], + addedComponents: [], + addedFunctions: [], + exposedComponents: [], + extensionPoints: [], + }, + }; + wrapper = ({ children }: { children: React.ReactNode }) => ( {children} @@ -219,6 +252,49 @@ describe('usePluginLinks()', () => { expect(log.warning).not.toHaveBeenCalled(); }); + // It can happen that core Grafana plugins (e.g. traces) reuse core components which implement extension points. + it('should not validate the extension point meta-info for core plugins', () => { + jest.mocked(isGrafanaDevMode).mockReturnValue(true); + + const linkConfig = { + targets: extensionPointId, + title: '1', + description: '1', + path: `/a/${pluginId}/2`, + }; + + // The `AddedLinksRegistry` is validating if the link is registered in the plugin metadata (config.apps). + config.apps[pluginId].extensions.addedLinks = [linkConfig]; + + wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + + ); + + registries.addedLinksRegistry.register({ + pluginId, + configs: [linkConfig], + }); + + // Trying to render an extension point that is not defined in the plugin meta + // (No restrictions due to being a core plugin) + let { result } = renderHook(() => usePluginLinks({ extensionPointId }), { wrapper }); + expect(result.current.links.length).toBe(1); + expect(log.warning).not.toHaveBeenCalled(); + }); + it('should not validate the extension point id in production mode', () => { // Empty list of extension points in the plugin meta (from plugin.json) wrapper = ({ children }: { children: React.ReactNode }) => ( diff --git a/public/app/features/plugins/extensions/usePluginLinks.tsx b/public/app/features/plugins/extensions/usePluginLinks.tsx index adec8114582..5d2b193a77c 100644 --- a/public/app/features/plugins/extensions/usePluginLinks.tsx +++ b/public/app/features/plugins/extensions/usePluginLinks.tsx @@ -34,19 +34,28 @@ export function usePluginLinks({ return useMemo(() => { const isInsidePlugin = Boolean(pluginContext); const pluginId = pluginContext?.meta.id ?? ''; + const isCoreGrafanaPlugin = pluginContext?.meta.module.startsWith('core:') ?? false; const pointLog = log.child({ pluginId, extensionPointId, }); - if (isGrafanaDevMode() && !isExtensionPointIdValid({ extensionPointId, pluginId, isInsidePlugin, log: pointLog })) { + if ( + isGrafanaDevMode() && + !isExtensionPointIdValid({ extensionPointId, pluginId, isInsidePlugin, isCoreGrafanaPlugin, log: pointLog }) + ) { return { isLoading: false, links: [], }; } - if (isGrafanaDevMode() && pluginContext && isExtensionPointMetaInfoMissing(extensionPointId, pluginContext)) { + if ( + isGrafanaDevMode() && + !isCoreGrafanaPlugin && + pluginContext && + isExtensionPointMetaInfoMissing(extensionPointId, pluginContext) + ) { pointLog.error(errors.EXTENSION_POINT_META_INFO_MISSING); return { isLoading: false, diff --git a/public/app/features/plugins/extensions/validators.test.tsx b/public/app/features/plugins/extensions/validators.test.tsx index 1b7e71eba7b..2f1713d3f34 100644 --- a/public/app/features/plugins/extensions/validators.test.tsx +++ b/public/app/features/plugins/extensions/validators.test.tsx @@ -217,6 +217,7 @@ describe('Plugin Extension Validators', () => { extensionPointId, pluginId, isInsidePlugin: pluginId !== 'grafana' && pluginId !== '', + isCoreGrafanaPlugin: false, log: createLogMock(), }) ).toBe(true); @@ -244,10 +245,23 @@ describe('Plugin Extension Validators', () => { extensionPointId, pluginId, isInsidePlugin: pluginId !== 'grafana' && pluginId !== '', + isCoreGrafanaPlugin: false, log: createLogMock(), }) ).toBe(false); }); + + it('should return FALSE true if the extension point id is set by a core plugin', () => { + expect( + isExtensionPointIdValid({ + extensionPointId: 'traces', + pluginId: 'traces', + isInsidePlugin: true, + isCoreGrafanaPlugin: true, + log: createLogMock(), + }) + ).toBe(true); + }); }); describe('isAddedLinkMetaInfoMissing()', () => { diff --git a/public/app/features/plugins/extensions/validators.ts b/public/app/features/plugins/extensions/validators.ts index 081ac98fbab..df808a34576 100644 --- a/public/app/features/plugins/extensions/validators.ts +++ b/public/app/features/plugins/extensions/validators.ts @@ -69,17 +69,19 @@ export function isExtensionPointIdValid({ extensionPointId, pluginId, isInsidePlugin, + isCoreGrafanaPlugin, log, }: { extensionPointId: string; pluginId: string; isInsidePlugin: boolean; + isCoreGrafanaPlugin: boolean; log: ExtensionsLog; }) { const startsWithPluginId = extensionPointId.startsWith(`${pluginId}/`) || extensionPointId.startsWith(`plugins/${pluginId}/`); - if (isInsidePlugin && !startsWithPluginId) { + if (isInsidePlugin && !isCoreGrafanaPlugin && !startsWithPluginId) { log.error(errors.INVALID_EXTENSION_POINT_ID_PLUGIN(pluginId, extensionPointId)); return false; } diff --git a/public/app/features/transformers/docs/content.ts b/public/app/features/transformers/docs/content.ts index 5f7d8e14703..273cbe22c94 100644 --- a/public/app/features/transformers/docs/content.ts +++ b/public/app/features/transformers/docs/content.ts @@ -1559,7 +1559,7 @@ ${buildImageContent( getHelperDocs: function (imageRenderType: ImageRenderType = ImageRenderType.ShortcodeFigure) { return ` Use this transformation to pivot the data frame, converting rows into columns and columns into rows. This transformation is particularly useful when you want to switch the orientation of your data to better suit your visualization needs. -If you have multiple types it will default to string type. +If you have multiple types, it will default to string type. You can select how empty cells should be represented. **Before Transformation:** diff --git a/public/app/features/transformers/editors/GroupingToMatrixTransformerEditor.tsx b/public/app/features/transformers/editors/GroupingToMatrixTransformerEditor.tsx index 6eb793e082a..dd8416c64b3 100644 --- a/public/app/features/transformers/editors/GroupingToMatrixTransformerEditor.tsx +++ b/public/app/features/transformers/editors/GroupingToMatrixTransformerEditor.tsx @@ -17,7 +17,7 @@ import { InlineField, InlineFieldRow, Select } from '@grafana/ui'; import { getTransformationContent } from '../docs/getTransformationContent'; import darkImage from '../images/dark/groupingToMatrix.svg'; import lightImage from '../images/light/groupingToMatrix.svg'; -import { useAllFieldNamesFromDataFrames } from '../utils'; +import { getEmptyOptions, useAllFieldNamesFromDataFrames } from '../utils'; export const GroupingToMatrixTransformerEditor = ({ input, @@ -61,49 +61,6 @@ export const GroupingToMatrixTransformerEditor = ({ [onChange, options] ); - const specialValueOptions: Array> = [ - { - label: t('transformers.grouping-to-matrix-transformer-editor.special-value-options.label.null', 'Null'), - value: SpecialValue.Null, - description: t( - 'transformers.grouping-to-matrix-transformer-editor.special-value-options.description.null-value', - 'Null value' - ), - }, - { - label: t('transformers.grouping-to-matrix-transformer-editor.special-value-options.label.true', 'True'), - value: SpecialValue.True, - description: t( - 'transformers.grouping-to-matrix-transformer-editor.special-value-options.description.boolean-true-value', - 'Boolean true value' - ), - }, - { - label: t('transformers.grouping-to-matrix-transformer-editor.special-value-options.label.false', 'False'), - value: SpecialValue.False, - description: t( - 'transformers.grouping-to-matrix-transformer-editor.special-value-options.description.boolean-false-value', - 'Boolean false value' - ), - }, - { - label: t('transformers.grouping-to-matrix-transformer-editor.special-value-options.label.zero', 'Zero'), - value: SpecialValue.Zero, - description: t( - 'transformers.grouping-to-matrix-transformer-editor.special-value-options.description.number-value', - 'Number 0 value' - ), - }, - { - label: t('transformers.grouping-to-matrix-transformer-editor.special-value-options.label.empty', 'Empty'), - value: SpecialValue.Empty, - description: t( - 'transformers.grouping-to-matrix-transformer-editor.special-value-options.description.empty-string', - 'Empty string' - ), - }, - ]; - const onSelectEmptyValue = useCallback( (value: SelectableValue) => { onChange({ @@ -143,7 +100,7 @@ export const GroupingToMatrixTransformerEditor = ({ /> - diff --git a/public/app/features/transformers/editors/TransposeTransformerEditor.tsx b/public/app/features/transformers/editors/TransposeTransformerEditor.tsx index baeee2461e5..fda90c7ee64 100644 --- a/public/app/features/transformers/editors/TransposeTransformerEditor.tsx +++ b/public/app/features/transformers/editors/TransposeTransformerEditor.tsx @@ -4,15 +4,25 @@ import { TransformerRegistryItem, TransformerUIProps, TransformerCategory, + SpecialValue, + SelectableValue, } from '@grafana/data'; import { TransposeTransformerOptions } from '@grafana/data/internal'; import { t } from '@grafana/i18n'; -import { InlineField, InlineFieldRow, Input } from '@grafana/ui'; +import { InlineField, InlineFieldRow, Input, Select } from '@grafana/ui'; import darkImage from '../images/dark/transpose.svg'; import lightImage from '../images/light/transpose.svg'; +import { getEmptyOptions } from '../utils'; export const TransposeTransformerEditor = ({ options, onChange }: TransformerUIProps) => { + const onSelectEmptyValue = (value?: SelectableValue) => { + onChange({ + ...options, + emptyValue: value?.value, + }); + }; + return ( <> @@ -42,6 +52,11 @@ export const TransposeTransformerEditor = ({ options, onChange }: TransformerUIP /> + + +