mirror of https://github.com/grafana/grafana.git
K8s: Remove kubernetesClientDashboardsFolders feature flag (#108626)
Actionlint / Lint GitHub Actions files (push) Waiting to run
Details
Backend Code Checks / Validate Backend Configs (push) Waiting to run
Details
Backend Unit Tests / Detect whether code changed (push) Waiting to run
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions
Details
Backend Unit Tests / All backend unit tests complete (push) Blocked by required conditions
Details
CodeQL checks / Analyze (actions) (push) Waiting to run
Details
CodeQL checks / Analyze (go) (push) Waiting to run
Details
CodeQL checks / Analyze (javascript) (push) Waiting to run
Details
Lint Frontend / Detect whether code changed (push) Waiting to run
Details
Lint Frontend / Lint (push) Blocked by required conditions
Details
Lint Frontend / Typecheck (push) Blocked by required conditions
Details
Lint Frontend / Betterer (push) Blocked by required conditions
Details
golangci-lint / lint-go (push) Waiting to run
Details
Verify i18n / verify-i18n (push) Waiting to run
Details
Documentation / Build & Verify Docs (push) Waiting to run
Details
End-to-end tests / Detect whether code changed (push) Waiting to run
Details
End-to-end tests / Build & Package Grafana (push) Blocked by required conditions
Details
End-to-end tests / Build E2E test runner (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/dashboards-suite, dashboards-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/panels-suite, panels-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/smoke-tests-suite, smoke-tests-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/various-suite, various-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (e2e/dashboards-suite, dashboards-suite) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (e2e/panels-suite, panels-suite) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (e2e/various-suite, various-suite) (push) Blocked by required conditions
Details
End-to-end tests / Verify Storybook (Playwright) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (1, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (2, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (3, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (4, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (5, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (6, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (7, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (8, 8) (push) Blocked by required conditions
Details
End-to-end tests / All Playwright tests complete (push) Blocked by required conditions
Details
End-to-end tests / A11y test (push) Blocked by required conditions
Details
End-to-end tests / All E2E tests complete (push) Blocked by required conditions
Details
Frontend tests / Detect whether code changed (push) Waiting to run
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (1) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (2) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (3) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (4) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (5) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (6) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (7) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (8) (push) Blocked by required conditions
Details
Frontend tests / Decoupled plugin tests (push) Blocked by required conditions
Details
Frontend tests / All frontend unit tests complete (push) Blocked by required conditions
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (1/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (2/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (3/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (4/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (5/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (6/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (7/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (8/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (1/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (2/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (3/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (4/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (5/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (6/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (7/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (8/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (1/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (2/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (3/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (4/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (5/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (6/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (7/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (8/8) (push) Waiting to run
Details
Integration Tests / All backend integration tests complete (push) Blocked by required conditions
Details
publish-technical-documentation-next / sync (push) Waiting to run
Details
Reject GitHub secrets / reject-gh-secrets (push) Waiting to run
Details
Build Release Packages / setup (push) Waiting to run
Details
Build Release Packages / Dispatch grafana-enterprise build (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:darwin/amd64, darwin-amd64, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:darwin/arm64, darwin-arm64, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/amd64,deb:grafana:linux/amd64,rpm:grafana:linux/amd64,docker:grafana:linux/amd64,docker:grafana:linux/amd64:ubuntu,npm:grafana,storybook, linux-amd64, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm/v6,deb:grafana:linux/arm/v6, linux-armv6, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm/v7,deb:grafana:linux/arm/v7,docker:grafana:linux/arm/v7,docker:grafana:linux/arm/v7:ubuntu, linux-armv7, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm64,deb:grafana:linux/arm64,rpm:grafana:linux/arm64,docker:grafana:linux/arm64,docker:grafana:linux/arm64:ubuntu, linux-arm64, false) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/s390x,deb:grafana:linux/s390x,rpm:grafana:linux/s390x,docker:grafana:linux/s390x,docker:grafana:linux/s390x:ubuntu, linux-s390x, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:windows/amd64,zip:grafana:windows/amd64,msi:grafana:windows/amd64, windows-amd64, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:windows/arm64,zip:grafana:windows/arm64, windows-arm64, true) (push) Blocked by required conditions
Details
Build Release Packages / Upload artifacts (push) Blocked by required conditions
Details
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Waiting to run
Details
Shellcheck / Shellcheck scripts (push) Waiting to run
Details
Run Storybook a11y tests / Detect whether code changed (push) Waiting to run
Details
Run Storybook a11y tests / Run Storybook a11y tests (push) Blocked by required conditions
Details
Swagger generated code / Verify committed API specs match (push) Waiting to run
Details
Dispatch sync to mirror / dispatch-job (push) Waiting to run
Details
Actionlint / Lint GitHub Actions files (push) Waiting to run
Details
Backend Code Checks / Validate Backend Configs (push) Waiting to run
Details
Backend Unit Tests / Detect whether code changed (push) Waiting to run
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions
Details
Backend Unit Tests / All backend unit tests complete (push) Blocked by required conditions
Details
CodeQL checks / Analyze (actions) (push) Waiting to run
Details
CodeQL checks / Analyze (go) (push) Waiting to run
Details
CodeQL checks / Analyze (javascript) (push) Waiting to run
Details
Lint Frontend / Detect whether code changed (push) Waiting to run
Details
Lint Frontend / Lint (push) Blocked by required conditions
Details
Lint Frontend / Typecheck (push) Blocked by required conditions
Details
Lint Frontend / Betterer (push) Blocked by required conditions
Details
golangci-lint / lint-go (push) Waiting to run
Details
Verify i18n / verify-i18n (push) Waiting to run
Details
Documentation / Build & Verify Docs (push) Waiting to run
Details
End-to-end tests / Detect whether code changed (push) Waiting to run
Details
End-to-end tests / Build & Package Grafana (push) Blocked by required conditions
Details
End-to-end tests / Build E2E test runner (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/dashboards-suite, dashboards-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/panels-suite, panels-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/smoke-tests-suite, smoke-tests-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/various-suite, various-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (e2e/dashboards-suite, dashboards-suite) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (e2e/panels-suite, panels-suite) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (e2e/various-suite, various-suite) (push) Blocked by required conditions
Details
End-to-end tests / Verify Storybook (Playwright) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (1, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (2, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (3, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (4, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (5, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (6, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (7, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (8, 8) (push) Blocked by required conditions
Details
End-to-end tests / All Playwright tests complete (push) Blocked by required conditions
Details
End-to-end tests / A11y test (push) Blocked by required conditions
Details
End-to-end tests / All E2E tests complete (push) Blocked by required conditions
Details
Frontend tests / Detect whether code changed (push) Waiting to run
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (1) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (2) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (3) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (4) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (5) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (6) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (7) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.chunk }} / 8) (8) (push) Blocked by required conditions
Details
Frontend tests / Decoupled plugin tests (push) Blocked by required conditions
Details
Frontend tests / All frontend unit tests complete (push) Blocked by required conditions
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (1/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (2/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (3/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (4/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (5/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (6/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (7/8) (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (8/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (1/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (2/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (3/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (4/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (5/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (6/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (7/8) (push) Waiting to run
Details
Integration Tests / MySQL (${{ matrix.shard }}) (8/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (1/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (2/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (3/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (4/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (5/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (6/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (7/8) (push) Waiting to run
Details
Integration Tests / Postgres (${{ matrix.shard }}) (8/8) (push) Waiting to run
Details
Integration Tests / All backend integration tests complete (push) Blocked by required conditions
Details
publish-technical-documentation-next / sync (push) Waiting to run
Details
Reject GitHub secrets / reject-gh-secrets (push) Waiting to run
Details
Build Release Packages / setup (push) Waiting to run
Details
Build Release Packages / Dispatch grafana-enterprise build (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:darwin/amd64, darwin-amd64, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:darwin/arm64, darwin-arm64, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/amd64,deb:grafana:linux/amd64,rpm:grafana:linux/amd64,docker:grafana:linux/amd64,docker:grafana:linux/amd64:ubuntu,npm:grafana,storybook, linux-amd64, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm/v6,deb:grafana:linux/arm/v6, linux-armv6, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm/v7,deb:grafana:linux/arm/v7,docker:grafana:linux/arm/v7,docker:grafana:linux/arm/v7:ubuntu, linux-armv7, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/arm64,deb:grafana:linux/arm64,rpm:grafana:linux/arm64,docker:grafana:linux/arm64,docker:grafana:linux/arm64:ubuntu, linux-arm64, false) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:linux/s390x,deb:grafana:linux/s390x,rpm:grafana:linux/s390x,docker:grafana:linux/s390x,docker:grafana:linux/s390x:ubuntu, linux-s390x, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:windows/amd64,zip:grafana:windows/amd64,msi:grafana:windows/amd64, windows-amd64, true) (push) Blocked by required conditions
Details
Build Release Packages / ${{ needs.setup.outputs.version }} / ${{ matrix.name }} (targz:grafana:windows/arm64,zip:grafana:windows/arm64, windows-arm64, true) (push) Blocked by required conditions
Details
Build Release Packages / Upload artifacts (push) Blocked by required conditions
Details
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Waiting to run
Details
Shellcheck / Shellcheck scripts (push) Waiting to run
Details
Run Storybook a11y tests / Detect whether code changed (push) Waiting to run
Details
Run Storybook a11y tests / Run Storybook a11y tests (push) Blocked by required conditions
Details
Swagger generated code / Verify committed API specs match (push) Waiting to run
Details
Dispatch sync to mirror / dispatch-job (push) Waiting to run
Details
This commit is contained in:
parent
e3cb84bef8
commit
1f025fe1a3
|
@ -43,7 +43,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
|
|||
| `dashgpt` | Enable AI powered features in dashboards | Yes |
|
||||
| `panelMonitoring` | Enables panel monitoring through logs and measurements | Yes |
|
||||
| `formatString` | Enable format string transformer | Yes |
|
||||
| `kubernetesClientDashboardsFolders` | Route the folder and dashboard service requests to k8s | Yes |
|
||||
| `addFieldFromCalculationStatFunctions` | Add cumulative and window functions to the add field from calculation transformation | Yes |
|
||||
| `annotationPermissionUpdate` | Change the way annotation permissions work by scoping them to folders and dashboards. | Yes |
|
||||
| `dashboardSceneForViewers` | Enables dashboard rendering using Scenes for viewer roles | Yes |
|
||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
|||
unifiedStorageSearchSprinkles = true
|
||||
kubernetesFoldersServiceV2 = true
|
||||
unifiedStorageSearchPermissionFiltering = true
|
||||
kubernetesClientDashboardsFolders = true
|
||||
|
||||
[unified_storage.folders.folder.grafana.app]
|
||||
dualWriterMode = 0
|
||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
|||
unifiedStorageSearchSprinkles = true
|
||||
kubernetesFoldersServiceV2 = true
|
||||
unifiedStorageSearchPermissionFiltering = true
|
||||
kubernetesClientDashboardsFolders = true
|
||||
|
||||
[unified_storage.folders.folder.grafana.app]
|
||||
dualWriterMode = 1
|
||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
|||
unifiedStorageSearchSprinkles = true
|
||||
kubernetesFoldersServiceV2 = true
|
||||
unifiedStorageSearchPermissionFiltering = true
|
||||
kubernetesClientDashboardsFolders = true
|
||||
|
||||
[unified_storage.folders.folder.grafana.app]
|
||||
dualWriterMode = 2
|
||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
|||
unifiedStorageSearchSprinkles = true
|
||||
kubernetesFoldersServiceV2 = true
|
||||
unifiedStorageSearchPermissionFiltering = true
|
||||
kubernetesClientDashboardsFolders = true
|
||||
|
||||
[unified_storage.folders.folder.grafana.app]
|
||||
dualWriterMode = 2
|
||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
|||
unifiedStorageSearchSprinkles = true
|
||||
kubernetesFoldersServiceV2 = true
|
||||
unifiedStorageSearchPermissionFiltering = true
|
||||
kubernetesClientDashboardsFolders = true
|
||||
|
||||
[unified_storage.folders.folder.grafana.app]
|
||||
dualWriterMode = 3
|
||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
|||
unifiedStorageSearchSprinkles = true
|
||||
kubernetesFoldersServiceV2 = true
|
||||
unifiedStorageSearchPermissionFiltering = true
|
||||
kubernetesClientDashboardsFolders = true
|
||||
|
||||
[unified_storage.folders.folder.grafana.app]
|
||||
dualWriterMode = 4
|
||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
|||
unifiedStorageSearchSprinkles = true
|
||||
kubernetesFoldersServiceV2 = true
|
||||
unifiedStorageSearchPermissionFiltering = true
|
||||
kubernetesClientDashboardsFolders = true
|
||||
|
||||
[unified_storage.folders.folder.grafana.app]
|
||||
dualWriterMode = 5
|
||||
|
|
|
@ -290,11 +290,6 @@ export interface FeatureToggles {
|
|||
*/
|
||||
kubernetesDashboards?: boolean;
|
||||
/**
|
||||
* Route the folder and dashboard service requests to k8s
|
||||
* @default true
|
||||
*/
|
||||
kubernetesClientDashboardsFolders?: boolean;
|
||||
/**
|
||||
* Disable schema validation for dashboards/v1
|
||||
*/
|
||||
dashboardDisableSchemaValidationV1?: boolean;
|
||||
|
|
|
@ -204,7 +204,7 @@ func (hs *HTTPServer) GetDashboard(c *contextmodel.ReqContext) response.Response
|
|||
}
|
||||
metrics.MFolderIDsAPICount.WithLabelValues(metrics.GetDashboard).Inc()
|
||||
// lookup folder title & url
|
||||
if dash.FolderUID != "" && hs.Features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
if dash.FolderUID != "" {
|
||||
queryResult, err := hs.folderService.Get(ctx, &folder.GetFolderQuery{
|
||||
OrgID: c.GetOrgID(),
|
||||
UID: &dash.FolderUID,
|
||||
|
|
|
@ -19,32 +19,23 @@ import (
|
|||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/serverlock"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/client"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/service"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
libraryelementsfake "github.com/grafana/grafana/pkg/services/libraryelements/fake"
|
||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||
|
@ -59,14 +50,10 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/publicdashboards/api"
|
||||
publicdashboardModels "github.com/grafana/grafana/pkg/services/publicdashboards/models"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/search/sort"
|
||||
"github.com/grafana/grafana/pkg/services/star/startest"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/user/usertest"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
"github.com/grafana/grafana/pkg/web/webtest"
|
||||
)
|
||||
|
@ -712,25 +699,42 @@ func TestIntegrationDashboardAPIEndpoint(t *testing.T) {
|
|||
|
||||
t.Run("Given provisioned dashboard", func(t *testing.T) {
|
||||
mockSQLStore := dbtest.NewFakeDB()
|
||||
dashboardStore := dashboards.NewFakeDashboardStore(t)
|
||||
dashboardStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioningSearchResults{ExternalID: "/dashboard1.json"}, nil).Once()
|
||||
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardProvisioningService := dashboards.NewFakeDashboardProvisioning(t)
|
||||
|
||||
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "editable": true, "style": "dark"}`))
|
||||
require.NoError(t, err)
|
||||
qResult := &dashboards.Dashboard{ID: 1, Data: dataValue}
|
||||
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil)
|
||||
|
||||
dashboardProvisioningService.On("GetProvisionedDashboardDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioning{ExternalID: "/dashboard1.json"}, nil)
|
||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", org.RoleEditor, func(sc *scenarioContext) {
|
||||
fakeProvisioningService := provisioning.NewProvisioningServiceMock(context.Background())
|
||||
fakeProvisioningService.GetDashboardProvisionerResolvedPathFunc = func(name string) string {
|
||||
return "/tmp/grafana/dashboards"
|
||||
}
|
||||
|
||||
dash := getDashboardShouldReturn200WithConfig(t, sc, fakeProvisioningService, dashboardStore, dashboardService, nil)
|
||||
hs := &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
ProvisioningService: fakeProvisioningService,
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||
dashboardProvisioningService: dashboardProvisioningService,
|
||||
SQLStore: mockSQLStore,
|
||||
AccessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||
DashboardService: dashboardService,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
starService: startest.NewStarServiceFake(),
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
}
|
||||
hs.callGetDashboard(sc)
|
||||
|
||||
assert.Equal(t, "../../../dashboard1.json", dash.Meta.ProvisionedExternalId, mockSQLStore)
|
||||
assert.Equal(t, http.StatusOK, sc.resp.Code)
|
||||
|
||||
dash := dtos.DashboardFullWithMeta{}
|
||||
err := json.NewDecoder(sc.resp.Body).Decode(&dash)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "../../../dashboard1.json", dash.Meta.ProvisionedExternalId)
|
||||
}, mockSQLStore)
|
||||
|
||||
loggedInUserScenarioWithRole(t, "When allowUiUpdates is true and calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", org.RoleEditor, func(sc *scenarioContext) {
|
||||
|
@ -748,7 +752,7 @@ func TestIntegrationDashboardAPIEndpoint(t *testing.T) {
|
|||
ProvisioningService: fakeProvisioningService,
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||
dashboardProvisioningService: mockDashboardProvisioningService{},
|
||||
dashboardProvisioningService: dashboardProvisioningService,
|
||||
SQLStore: mockSQLStore,
|
||||
AccessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||
DashboardService: dashboardService,
|
||||
|
@ -771,6 +775,7 @@ func TestIntegrationDashboardAPIEndpoint(t *testing.T) {
|
|||
t.Run("v2 dashboards should not be returned in api", func(t *testing.T) {
|
||||
mockSQLStore := dbtest.NewFakeDB()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardProvisioningService := dashboards.NewFakeDashboardProvisioning(t)
|
||||
|
||||
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "apiVersion": "v2"}`))
|
||||
require.NoError(t, err)
|
||||
|
@ -794,7 +799,7 @@ func TestIntegrationDashboardAPIEndpoint(t *testing.T) {
|
|||
Features: featuremgmt.WithFeatures(),
|
||||
starService: startest.NewStarServiceFake(),
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
dashboardProvisioningService: mockDashboardProvisioningService{},
|
||||
dashboardProvisioningService: dashboardProvisioningService,
|
||||
folderService: foldertest.NewFakeService(),
|
||||
log: log.New("test"),
|
||||
namespacer: func(orgID int64) string { return strconv.FormatInt(orgID, 10) },
|
||||
|
@ -912,79 +917,6 @@ func TestDashboardVersionsAPIEndpoint(t *testing.T) {
|
|||
}, mockSQLStore)
|
||||
}
|
||||
|
||||
func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, provisioningService provisioning.ProvisioningService, dashboardStore dashboards.Store, dashboardService dashboards.DashboardService, folderStore folder.FolderStore) dtos.DashboardFullWithMeta {
|
||||
t.Helper()
|
||||
|
||||
if provisioningService == nil {
|
||||
provisioningService = provisioning.NewProvisioningServiceMock(context.Background())
|
||||
}
|
||||
|
||||
features := featuremgmt.WithFeatures()
|
||||
var err error
|
||||
if dashboardStore == nil {
|
||||
sql, cfg := db.InitTestDBWithCfg(t)
|
||||
dashboardStore, err = database.ProvideDashboardStore(sql, cfg, features, tagimpl.ProvideService(sql))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
libraryPanelsService := mockLibraryPanelService{}
|
||||
libraryElementsService := libraryelementsfake.LibraryElementService{}
|
||||
cfg := setting.NewCfg()
|
||||
ac := accesscontrolmock.New()
|
||||
folderPermissions := accesscontrolmock.NewMockedPermissionsService()
|
||||
dashboardPermissions := accesscontrolmock.NewMockedPermissionsService()
|
||||
|
||||
db := db.InitTestDB(t)
|
||||
fStore := folderimpl.ProvideStore(db)
|
||||
quotaService := quotatest.New(false, nil)
|
||||
folderSvc := folderimpl.ProvideService(
|
||||
fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore,
|
||||
nil, db, features, supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil,
|
||||
dualwrite.ProvideTestService(), sort.ProvideService(), apiserver.WithoutRestConfig)
|
||||
if dashboardService == nil {
|
||||
dashboardService, err = service.ProvideDashboardServiceImpl(
|
||||
cfg, dashboardStore, folderStore, features, folderPermissions,
|
||||
ac, actest.FakeService{}, folderSvc, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil,
|
||||
dualwrite.ProvideTestService(), sort.ProvideService(),
|
||||
serverlock.ProvideService(db, tracing.InitializeTracerForTest()), kvstore.NewFakeKVStore(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
dashboardService.(dashboards.PermissionsRegistrationService).RegisterDashboardPermissions(dashboardPermissions)
|
||||
}
|
||||
|
||||
dashboardProvisioningService, err := service.ProvideDashboardServiceImpl(
|
||||
cfg, dashboardStore, folderStore, features, folderPermissions,
|
||||
ac, actest.FakeService{}, folderSvc, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil,
|
||||
dualwrite.ProvideTestService(), sort.ProvideService(),
|
||||
serverlock.ProvideService(db, tracing.InitializeTracerForTest()), kvstore.NewFakeKVStore(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
hs := &HTTPServer{
|
||||
Cfg: cfg,
|
||||
LibraryPanelService: &libraryPanelsService,
|
||||
LibraryElementService: &libraryElementsService,
|
||||
SQLStore: sc.sqlStore,
|
||||
ProvisioningService: provisioningService,
|
||||
AccessControl: accesscontrolmock.New(),
|
||||
dashboardProvisioningService: dashboardProvisioningService,
|
||||
DashboardService: dashboardService,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
starService: startest.NewStarServiceFake(),
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
}
|
||||
|
||||
hs.callGetDashboard(sc)
|
||||
|
||||
require.Equal(sc.t, 200, sc.resp.Code)
|
||||
|
||||
dash := dtos.DashboardFullWithMeta{}
|
||||
err = json.NewDecoder(sc.resp.Body).Decode(&dash)
|
||||
require.NoError(sc.t, err)
|
||||
|
||||
return dash
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) callGetDashboard(sc *scenarioContext) {
|
||||
sc.handlerFunc = hs.GetDashboard
|
||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/api/apierrors"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
|
@ -20,7 +17,6 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
|
@ -198,13 +194,6 @@ func (hs *HTTPServer) CreateFolder(c *contextmodel.ReqContext) response.Response
|
|||
return apierrors.ToFolderErrorResponse(err)
|
||||
}
|
||||
|
||||
// Only set default permissions if the Folder API Server is disabled.
|
||||
if !hs.Features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
if err := hs.setDefaultFolderPermissions(c.Req.Context(), cmd.OrgID, cmd.SignedInUser, folder); err != nil {
|
||||
hs.log.Error("Could not set the default folder permissions", "folder", folder.Title, "user", cmd.SignedInUser, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
folderDTO, err := hs.newToFolderDto(c, folder)
|
||||
if err != nil {
|
||||
return response.Err(err)
|
||||
|
@ -214,46 +203,6 @@ func (hs *HTTPServer) CreateFolder(c *contextmodel.ReqContext) response.Response
|
|||
return response.JSON(http.StatusOK, folderDTO)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) setDefaultFolderPermissions(ctx context.Context, orgID int64, user identity.Requester, folder *folder.Folder) error {
|
||||
if !hs.Cfg.RBAC.PermissionsOnCreation("folder") {
|
||||
return nil
|
||||
}
|
||||
|
||||
var permissions []accesscontrol.SetResourcePermissionCommand
|
||||
|
||||
if user.IsIdentityType(claims.TypeUser, claims.TypeServiceAccount) {
|
||||
userID, err := user.GetInternalID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{
|
||||
UserID: userID, Permission: dashboardaccess.PERMISSION_ADMIN.String(),
|
||||
})
|
||||
}
|
||||
|
||||
isNested := folder.ParentUID != ""
|
||||
if !isNested || !hs.Features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
permissions = append(permissions, []accesscontrol.SetResourcePermissionCommand{
|
||||
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
|
||||
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
|
||||
}...)
|
||||
}
|
||||
|
||||
_, err := hs.folderPermissionsService.SetPermissions(ctx, orgID, folder.UID, permissions...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.IsIdentityType(claims.TypeUser, claims.TypeServiceAccount) {
|
||||
// Clear permission cache for the user who's created the folder, so that new permissions are fetched for their next call
|
||||
// Required for cases when caller wants to immediately interact with the newly created object
|
||||
hs.accesscontrolService.ClearUserPermissionCache(user)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// swagger:route POST /folders/{folder_uid}/move folders moveFolder
|
||||
//
|
||||
// Move folder.
|
||||
|
|
|
@ -620,9 +620,6 @@ func TestGetFolderLegacyAndUnifiedStorage(t *testing.T) {
|
|||
}
|
||||
|
||||
featuresArr := []any{featuremgmt.FlagNestedFolders}
|
||||
if tc.unifiedStorageEnabled {
|
||||
featuresArr = append(featuresArr, featuremgmt.FlagKubernetesClientDashboardsFolders)
|
||||
}
|
||||
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.Cfg = cfg
|
||||
|
@ -676,39 +673,6 @@ func TestGetFolderLegacyAndUnifiedStorage(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSetDefaultPermissionsWhenCreatingFolder(t *testing.T) {
|
||||
folderService := &foldertest.FakeService{}
|
||||
folderWithoutParentInput := "{ \"uid\": \"uid\", \"title\": \"Folder\"}"
|
||||
|
||||
type testCase struct {
|
||||
description string
|
||||
expectedCallsToSetPermissions int
|
||||
expectedCode int
|
||||
expectedFolder *folder.Folder
|
||||
permissions []accesscontrol.Permission
|
||||
featuresArr []any
|
||||
input string
|
||||
}
|
||||
|
||||
tcs := []testCase{
|
||||
{
|
||||
description: "folder creation succeeds, via legacy storage",
|
||||
expectedCallsToSetPermissions: 1,
|
||||
input: folderWithoutParentInput,
|
||||
expectedCode: http.StatusOK,
|
||||
expectedFolder: &folder.Folder{UID: "uid", Title: "Folder"},
|
||||
permissions: []accesscontrol.Permission{{Action: dashboards.ActionFoldersCreate}},
|
||||
},
|
||||
{
|
||||
description: "folder creation succeeds, via API Server",
|
||||
expectedCallsToSetPermissions: 0,
|
||||
input: folderWithoutParentInput,
|
||||
expectedCode: http.StatusOK,
|
||||
expectedFolder: &folder.Folder{UID: "uid", Title: "Folder"},
|
||||
permissions: []accesscontrol.Permission{{Action: dashboards.ActionFoldersCreate}},
|
||||
featuresArr: []any{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
},
|
||||
}
|
||||
|
||||
// we need to save these values because they are defined at `setting` package level
|
||||
// and modified when we invoke setting.NewCfgFromINIFile
|
||||
prevCookieSameSiteDisabled := setting.CookieSameSiteDisabled
|
||||
|
@ -725,42 +689,32 @@ func TestSetDefaultPermissionsWhenCreatingFolder(t *testing.T) {
|
|||
setting.CookieSameSiteDisabled = prevCookieSameSiteDisabled
|
||||
setting.CookieSameSiteMode = prevCookieSameSiteMode
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
folderService.ExpectedFolder = tc.expectedFolder
|
||||
folderService := &foldertest.FakeService{
|
||||
ExpectedFolder: &folder.Folder{UID: "uid", Title: "Folder"},
|
||||
}
|
||||
folderPermService := acmock.NewMockedPermissionsService()
|
||||
folderPermService.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
|
||||
|
||||
srv := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.Cfg = cfg
|
||||
|
||||
featuresArr := append(tc.featuresArr, featuremgmt.FlagNestedFolders)
|
||||
hs.Features = featuremgmt.WithFeatures(
|
||||
featuresArr...,
|
||||
)
|
||||
hs.Features = featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
||||
hs.folderService = folderService
|
||||
hs.folderPermissionsService = folderPermService
|
||||
hs.accesscontrolService = actest.FakeService{}
|
||||
})
|
||||
|
||||
input := strings.NewReader(tc.input)
|
||||
input := strings.NewReader("{ \"uid\": \"uid\", \"title\": \"Folder\"}")
|
||||
req := srv.NewPostRequest("/api/folders", input)
|
||||
req = webtest.RequestWithSignedInUser(req, userWithPermissions(1, tc.permissions))
|
||||
req = webtest.RequestWithSignedInUser(req, userWithPermissions(1, []accesscontrol.Permission{{Action: dashboards.ActionFoldersCreate}}))
|
||||
resp, err := srv.SendJSON(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expectedCode, resp.StatusCode)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
folder := dtos.Folder{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&folder)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
|
||||
folderPermService.AssertNumberOfCalls(t, "SetPermissions", tc.expectedCallsToSetPermissions)
|
||||
|
||||
if tc.expectedCode == http.StatusOK {
|
||||
assert.Equal(t, "uid", folder.UID)
|
||||
assert.Equal(t, "Folder", folder.Title)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -872,11 +872,9 @@ func setUpServiceTest(t *testing.T, withDashboardMock bool, cfgOverrides ...conf
|
|||
rr := routing.NewRouteRegister()
|
||||
tracer := tracing.InitializeTracerForTest()
|
||||
|
||||
fakeFolder := &folder.Folder{UID: "folderUID", Title: "Folder"}
|
||||
mockFolder := &foldertest.FakeService{
|
||||
ExpectedFolders: []*folder.Folder{fakeFolder},
|
||||
ExpectedFolder: fakeFolder,
|
||||
}
|
||||
fakeFolder := &folder.Folder{UID: "folderUID", Title: "Folder", Fullpath: "Folder"}
|
||||
mockFolder := foldertest.NewFakeService()
|
||||
mockFolder.AddFolder(fakeFolder)
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
section, err := cfg.Raw.NewSection("cloud_migration")
|
||||
|
|
|
@ -3,6 +3,7 @@ package cloudmigrationimpl
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
@ -18,6 +19,7 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
|
@ -408,6 +410,9 @@ func updateNotificationPolicyTree(t *testing.T, ctx context.Context, service *Se
|
|||
func createAlertRule(t *testing.T, ctx context.Context, service *Service, user *user.SignedInUser, isPaused bool, ruleGroup string) models.AlertRule {
|
||||
t.Helper()
|
||||
|
||||
// Ensure the folder exists before creating alert rules
|
||||
createFolder(t, ctx, service, user, "folderUID", "Test Folder")
|
||||
|
||||
rule := models.AlertRule{
|
||||
OrgID: user.GetOrgID(),
|
||||
Title: fmt.Sprintf("Alert Rule SLO (Paused: %v) - %v", isPaused, ruleGroup),
|
||||
|
@ -437,6 +442,19 @@ func createAlertRule(t *testing.T, ctx context.Context, service *Service, user *
|
|||
return createdRule
|
||||
}
|
||||
|
||||
func createFolder(t *testing.T, ctx context.Context, service *Service, user *user.SignedInUser, uid, title string) {
|
||||
t.Helper()
|
||||
_, err := service.folderService.Create(ctx, &folder.CreateFolderCommand{
|
||||
OrgID: user.GetOrgID(),
|
||||
UID: uid,
|
||||
Title: title,
|
||||
SignedInUser: user,
|
||||
})
|
||||
if err != nil && !errors.Is(err, dashboards.ErrFolderWithSameUIDExists) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func createAlertRuleGroup(t *testing.T, ctx context.Context, service *Service, user *user.SignedInUser, title string, rules []models.AlertRule) models.AlertRuleGroup {
|
||||
t.Helper()
|
||||
|
||||
|
|
|
@ -146,14 +146,6 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
|
|||
}
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
|
||||
|
||||
// in the k8s flow, we connect the library panels in pkg/registry/apis/dashboard/legacy/sql_dashboards.go
|
||||
if !s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
err = s.libraryPanelService.ConnectLibraryPanelsForDashboard(ctx, req.User, savedDashboard)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
revision := savedDashboard.Data.Get("revision").MustInt64(0)
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
|
||||
return &dashboardimport.ImportDashboardResponse{
|
||||
|
|
|
@ -46,16 +46,11 @@ func TestImportDashboardService(t *testing.T) {
|
|||
}
|
||||
|
||||
importLibraryPanelsForDashboard := false
|
||||
connectLibraryPanelsForDashboardCalled := false
|
||||
libraryPanelService := &libraryPanelServiceMock{
|
||||
importLibraryPanelsForDashboardFunc: func(ctx context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error {
|
||||
importLibraryPanelsForDashboard = true
|
||||
return nil
|
||||
},
|
||||
connectLibraryPanelsForDashboardFunc: func(ctx context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error {
|
||||
connectLibraryPanelsForDashboardCalled = true
|
||||
return nil
|
||||
},
|
||||
}
|
||||
folderService := &foldertest.FakeService{
|
||||
ExpectedFolder: &folder.Folder{
|
||||
|
@ -98,7 +93,6 @@ func TestImportDashboardService(t *testing.T) {
|
|||
require.Equal(t, "prom", panel.Get("datasource").MustString())
|
||||
|
||||
require.True(t, importLibraryPanelsForDashboard)
|
||||
require.True(t, connectLibraryPanelsForDashboardCalled)
|
||||
})
|
||||
|
||||
t.Run("When importing a non-plugin dashboard should save dashboard and sync library panels", func(t *testing.T) {
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/services/search/model"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
|
||||
|
@ -77,12 +76,8 @@ type DashboardProvisioningService interface {
|
|||
type Store interface {
|
||||
DeleteDashboard(ctx context.Context, cmd *DeleteDashboardCommand) error
|
||||
CleanupAfterDelete(ctx context.Context, cmd *DeleteDashboardCommand) error
|
||||
DeleteAllDashboards(ctx context.Context, orgID int64) error
|
||||
DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *DeleteOrphanedProvisionedDashboardsCommand) error
|
||||
FindDashboards(ctx context.Context, query *FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error)
|
||||
GetDashboard(ctx context.Context, query *GetDashboardQuery) (*Dashboard, error)
|
||||
GetDashboardUIDByID(ctx context.Context, query *GetDashboardRefByIDQuery) (*DashboardRef, error)
|
||||
GetDashboards(ctx context.Context, query *GetDashboardsQuery) ([]*Dashboard, error)
|
||||
// GetDashboardsByPluginID retrieves dashboards identified by plugin.
|
||||
GetDashboardsByPluginID(ctx context.Context, query *GetDashboardsByPluginIDQuery) ([]*Dashboard, error)
|
||||
GetDashboardTags(ctx context.Context, query *GetDashboardTagsQuery) ([]*DashboardTagCloudItem, error)
|
||||
|
@ -97,11 +92,7 @@ type Store interface {
|
|||
// ValidateDashboardBeforeSave validates a dashboard before save.
|
||||
ValidateDashboardBeforeSave(ctx context.Context, dashboard *Dashboard, overwrite bool) (bool, error)
|
||||
|
||||
Count(context.Context, *quota.ScopeParameters) (*quota.Map, error)
|
||||
CountInOrg(ctx context.Context, orgID int64, isFolder bool) (int64, error)
|
||||
// CountDashboardsInFolder returns the number of dashboards associated with
|
||||
// the given parent folder ID.
|
||||
CountDashboardsInFolders(ctx context.Context, request *CountDashboardsInFolderRequest) (int64, error)
|
||||
DeleteDashboardsInFolders(ctx context.Context, request *DeleteDashboardsInFolderRequest) error
|
||||
|
||||
GetAllDashboardsByOrgId(ctx context.Context, orgID int64) ([]*Dashboard, error)
|
||||
|
|
|
@ -2,10 +2,8 @@ package database
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
|
@ -25,7 +23,6 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/sqlstore/migrations"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
||||
"github.com/grafana/grafana/pkg/services/star"
|
||||
"github.com/grafana/grafana/pkg/services/store"
|
||||
"github.com/grafana/grafana/pkg/services/tag"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
@ -352,34 +349,6 @@ func (d *dashboardStore) UnprovisionDashboard(ctx context.Context, id int64) err
|
|||
})
|
||||
}
|
||||
|
||||
func (d *dashboardStore) DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *dashboards.DeleteOrphanedProvisionedDashboardsCommand) error {
|
||||
ctx, span := tracer.Start(ctx, "dashboards.database.DeleteOrphanedProvisionedDashboards")
|
||||
defer span.End()
|
||||
|
||||
return d.store.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
var result []*dashboards.DashboardProvisioning
|
||||
|
||||
convertedReaderNames := make([]any, len(cmd.ReaderNames))
|
||||
for index, readerName := range cmd.ReaderNames {
|
||||
convertedReaderNames[index] = readerName
|
||||
}
|
||||
|
||||
err := sess.NotIn("name", convertedReaderNames...).Find(&result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, deleteDashCommand := range result {
|
||||
err := d.DeleteDashboard(ctx, &dashboards.DeleteDashboardCommand{ID: deleteDashCommand.DashboardID})
|
||||
if err != nil && !errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (d *dashboardStore) Count(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
|
||||
ctx, span := tracer.Start(ctx, "dashboards.database.Count")
|
||||
defer span.End()
|
||||
|
@ -690,16 +659,6 @@ func (d *dashboardStore) CleanupAfterDelete(ctx context.Context, cmd *dashboards
|
|||
return err
|
||||
}
|
||||
|
||||
func (d *dashboardStore) DeleteAllDashboards(ctx context.Context, orgID int64) error {
|
||||
ctx, span := tracer.Start(ctx, "dashboards.database.DeleteAllDashboards")
|
||||
defer span.End()
|
||||
|
||||
return d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
||||
_, err := sess.Where("org_id = ?", orgID).Delete(&dashboards.Dashboard{})
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// FIXME: Remove me and handle nested deletions in the service with the DashboardPermissionsService
|
||||
func (d *dashboardStore) deleteResourcePermissions(sess *db.Session, orgID int64, resourceScope string) error {
|
||||
// retrieve all permissions for the resource scope and org id
|
||||
|
@ -821,58 +780,6 @@ func (d *dashboardStore) GetDashboard(ctx context.Context, query *dashboards.Get
|
|||
return queryResult, err
|
||||
}
|
||||
|
||||
func (d *dashboardStore) GetDashboardUIDByID(ctx context.Context, query *dashboards.GetDashboardRefByIDQuery) (*dashboards.DashboardRef, error) {
|
||||
ctx, span := tracer.Start(ctx, "dashboards.database.GetDashboardUIDByID")
|
||||
defer span.End()
|
||||
|
||||
us := &dashboards.DashboardRef{}
|
||||
err := d.store.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
var rawSQL = `SELECT uid, slug, folder_uid from dashboard WHERE Id=?`
|
||||
exists, err := sess.SQL(rawSQL, query.ID).Get(us)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !exists {
|
||||
return dashboards.ErrDashboardNotFound
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return us, nil
|
||||
}
|
||||
|
||||
func (d *dashboardStore) GetDashboards(ctx context.Context, query *dashboards.GetDashboardsQuery) ([]*dashboards.Dashboard, error) {
|
||||
ctx, span := tracer.Start(ctx, "dashboards.database.GetDashboards")
|
||||
defer span.End()
|
||||
|
||||
var dashboards = make([]*dashboards.Dashboard, 0)
|
||||
err := d.store.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
if len(query.DashboardIDs) == 0 && len(query.DashboardUIDs) == 0 {
|
||||
return star.ErrCommandValidationFailed
|
||||
}
|
||||
|
||||
// remove soft deleted dashboards from the response
|
||||
sess.Where("deleted IS NULL")
|
||||
|
||||
if len(query.DashboardIDs) > 0 {
|
||||
sess.In("id", query.DashboardIDs)
|
||||
} else {
|
||||
sess.In("uid", query.DashboardUIDs)
|
||||
}
|
||||
if query.OrgID > 0 {
|
||||
sess.Where("org_id = ?", query.OrgID)
|
||||
}
|
||||
|
||||
err := sess.Find(&dashboards)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dashboards, nil
|
||||
}
|
||||
|
||||
func (d *dashboardStore) FindDashboards(ctx context.Context, query *dashboards.FindPersistedDashboardsQuery) ([]dashboards.DashboardSearchProjection, error) {
|
||||
ctx, span := tracer.Start(ctx, "dashboards.database.FindDashboards")
|
||||
defer span.End()
|
||||
|
@ -1025,39 +932,6 @@ func (d *dashboardStore) GetDashboardTags(ctx context.Context, query *dashboards
|
|||
return queryResult, nil
|
||||
}
|
||||
|
||||
// CountDashboardsInFolder returns a count of all dashboards associated with the
|
||||
// given parent folder ID.
|
||||
func (d *dashboardStore) CountDashboardsInFolders(
|
||||
ctx context.Context, req *dashboards.CountDashboardsInFolderRequest) (int64, error) {
|
||||
ctx, span := tracer.Start(ctx, "dashboards.database.CountDashboardsInFolders")
|
||||
defer span.End()
|
||||
|
||||
if len(req.FolderUIDs) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
var count int64
|
||||
err := d.store.WithDbSession(ctx, func(sess *db.Session) error {
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Dashboard).Inc()
|
||||
s := strings.Builder{}
|
||||
args := make([]any, 0, 3)
|
||||
s.WriteString("SELECT COUNT(*) FROM dashboard WHERE ")
|
||||
if len(req.FolderUIDs) == 1 && req.FolderUIDs[0] == "" {
|
||||
s.WriteString("folder_uid IS NULL")
|
||||
} else {
|
||||
s.WriteString(fmt.Sprintf("folder_uid IN (%s)", strings.Repeat("?,", len(req.FolderUIDs)-1)+"?"))
|
||||
for _, folderUID := range req.FolderUIDs {
|
||||
args = append(args, folderUID)
|
||||
}
|
||||
}
|
||||
s.WriteString(" AND org_id = ? AND is_folder = ? AND deleted IS NULL")
|
||||
args = append(args, req.OrgID, d.store.GetDialect().BooleanValue(false))
|
||||
sql := s.String()
|
||||
_, err := sess.SQL(sql, args...).Get(&count)
|
||||
return err
|
||||
})
|
||||
return count, err
|
||||
}
|
||||
|
||||
func (d *dashboardStore) DeleteDashboardsInFolders(
|
||||
ctx context.Context, req *dashboards.DeleteDashboardsInFolderRequest) error {
|
||||
ctx, span := tracer.Start(ctx, "dashboards.database.DeleteDashboardsInFolders")
|
||||
|
|
|
@ -59,42 +59,6 @@ func TestIntegrationDashboardProvisioningTest(t *testing.T) {
|
|||
require.NotEqual(t, 0, dash.ID)
|
||||
dashId := dash.ID
|
||||
|
||||
t.Run("Deleting orphaned provisioned dashboards", func(t *testing.T) {
|
||||
saveCmd := dashboards.SaveDashboardCommand{
|
||||
OrgID: 1,
|
||||
IsFolder: false,
|
||||
FolderUID: dash.UID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||
"id": nil,
|
||||
"title": "another_dashboard",
|
||||
}),
|
||||
}
|
||||
|
||||
provisioning := &dashboards.DashboardProvisioning{
|
||||
Name: "another_reader",
|
||||
ExternalID: "/var/grafana.json",
|
||||
Updated: now.Unix(),
|
||||
}
|
||||
|
||||
anotherDash, err := dashboardStore.SaveProvisionedDashboard(context.Background(), saveCmd, provisioning)
|
||||
require.Nil(t, err)
|
||||
|
||||
query := &dashboards.GetDashboardsQuery{DashboardIDs: []int64{anotherDash.ID}}
|
||||
queryResult, err := dashboardStore.GetDashboards(context.Background(), query)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, queryResult)
|
||||
|
||||
deleteCmd := &dashboards.DeleteOrphanedProvisionedDashboardsCommand{ReaderNames: []string{"default"}}
|
||||
require.Nil(t, dashboardStore.DeleteOrphanedProvisionedDashboards(context.Background(), deleteCmd))
|
||||
|
||||
query = &dashboards.GetDashboardsQuery{DashboardIDs: []int64{dash.ID, anotherDash.ID}}
|
||||
queryResult, err = dashboardStore.GetDashboards(context.Background(), query)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, 1, len(queryResult))
|
||||
require.Equal(t, dashId, queryResult[0].ID)
|
||||
})
|
||||
|
||||
t.Run("Can query for provisioned dashboards", func(t *testing.T) {
|
||||
rslt, err := dashboardStore.GetProvisionedDashboardData(context.Background(), "default")
|
||||
require.Nil(t, err)
|
||||
|
|
|
@ -10,27 +10,19 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
libmodel "github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/search/model"
|
||||
"github.com/grafana/grafana/pkg/services/search/sort"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
@ -185,15 +177,6 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||
require.False(t, queryResult.IsFolder)
|
||||
})
|
||||
|
||||
t.Run("Should be able to get a dashboard UID by ID", func(t *testing.T) {
|
||||
setup()
|
||||
query := dashboards.GetDashboardRefByIDQuery{ID: savedDash.ID}
|
||||
queryResult, err := dashboardStore.GetDashboardUIDByID(context.Background(), &query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, queryResult.UID, savedDash.UID)
|
||||
require.Equal(t, queryResult.FolderUID, savedFolder.UID)
|
||||
})
|
||||
|
||||
t.Run("Shouldn't be able to get a dashboard with just an OrgID", func(t *testing.T) {
|
||||
setup()
|
||||
query := dashboards.GetDashboardQuery{
|
||||
|
@ -216,19 +199,6 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||
require.Error(t, err, dashboards.ErrDashboardNotFound)
|
||||
})
|
||||
|
||||
t.Run("Should be able to get dashboards by IDs & UIDs", func(t *testing.T) {
|
||||
setup()
|
||||
query := dashboards.GetDashboardsQuery{DashboardIDs: []int64{savedDash.ID, savedDash2.ID}}
|
||||
queryResult, err := dashboardStore.GetDashboards(context.Background(), &query)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(queryResult), 2)
|
||||
|
||||
query = dashboards.GetDashboardsQuery{DashboardUIDs: []string{savedDash.UID, savedDash2.UID}}
|
||||
queryResult, err = dashboardStore.GetDashboards(context.Background(), &query)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(queryResult), 2)
|
||||
})
|
||||
|
||||
t.Run("Should be able to delete dashboard and associated tags", func(t *testing.T) {
|
||||
setup()
|
||||
dash := insertTestDashboard(t, dashboardStore, "delete me", 1, 0, "", false, "delete this")
|
||||
|
@ -330,32 +300,6 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||
require.Len(t, res, 0)
|
||||
})
|
||||
|
||||
t.Run("Should be able to delete all dashboards for an org", func(t *testing.T) {
|
||||
setup()
|
||||
dash1 := insertTestDashboard(t, dashboardStore, "delete me", 1, 0, "", false, "delete this")
|
||||
dash2 := insertTestDashboard(t, dashboardStore, "delete me2", 1, 0, "", false, "delete this2")
|
||||
dash3 := insertTestDashboard(t, dashboardStore, "dont delete me", 2, 0, "", false, "dont delete me")
|
||||
|
||||
err := dashboardStore.DeleteAllDashboards(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// no dashboards should exist for org 1
|
||||
queryResult, err := dashboardStore.GetDashboards(context.Background(), &dashboards.GetDashboardsQuery{
|
||||
OrgID: 1,
|
||||
DashboardUIDs: []string{dash1.UID, dash2.UID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(queryResult), 0)
|
||||
|
||||
// but we should still have one for org 2
|
||||
queryResult, err = dashboardStore.GetDashboards(context.Background(), &dashboards.GetDashboardsQuery{
|
||||
OrgID: 2,
|
||||
DashboardUIDs: []string{dash3.UID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, len(queryResult), 1)
|
||||
})
|
||||
|
||||
t.Run("Should be able to get all dashboards for an org", func(t *testing.T) {
|
||||
setup()
|
||||
dash1 := insertTestDashboard(t, dashboardStore, "org3test1", 3, 0, "", false, "org 1 test 1")
|
||||
|
@ -679,22 +623,6 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||
require.Equal(t, len(hit2.Tags), 1)
|
||||
})
|
||||
|
||||
t.Run("Can count dashboards by parent folder", func(t *testing.T) {
|
||||
setup()
|
||||
// setup() saves one dashboard in the general folder and two in the "savedFolder".
|
||||
count, err := dashboardStore.CountDashboardsInFolders(
|
||||
context.Background(),
|
||||
&dashboards.CountDashboardsInFolderRequest{FolderUIDs: []string{""}, OrgID: 1})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), count)
|
||||
|
||||
count, err = dashboardStore.CountDashboardsInFolders(
|
||||
context.Background(),
|
||||
&dashboards.CountDashboardsInFolderRequest{FolderUIDs: []string{savedFolder.UID}, OrgID: 1})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(2), count)
|
||||
})
|
||||
|
||||
t.Run("Can delete dashboards in folder", func(t *testing.T) {
|
||||
setup()
|
||||
folder := insertTestDashboard(t, dashboardStore, "dash folder", 1, 0, "", true, "prod", "webapp")
|
||||
|
@ -703,10 +631,6 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
|||
|
||||
err := dashboardStore.DeleteDashboardsInFolders(context.Background(), &dashboards.DeleteDashboardsInFolderRequest{OrgID: folder.OrgID, FolderUIDs: []string{folder.UID}})
|
||||
require.NoError(t, err)
|
||||
|
||||
count, err := dashboardStore.CountDashboardsInFolders(context.Background(), &dashboards.CountDashboardsInFolderRequest{FolderUIDs: []string{folder.UID}, OrgID: 1})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, count, int64(0))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -830,6 +754,59 @@ func TestIntegrationDashboard_Filter(t *testing.T) {
|
|||
assert.Equal(t, dashB.ID, results[0].ID)
|
||||
}
|
||||
|
||||
// has to be added to both the folder & the dashboard table
|
||||
func insertTestFolder(t *testing.T, dashboardStore dashboards.Store, sqlStore db.DB, title string, orgId int64, parentUID string, tags ...interface{}) *dashboards.Dashboard {
|
||||
t.Helper()
|
||||
|
||||
cmd := dashboards.SaveDashboardCommand{
|
||||
OrgID: orgId,
|
||||
FolderUID: parentUID,
|
||||
IsFolder: true,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": title,
|
||||
"tags": tags,
|
||||
}),
|
||||
}
|
||||
dash, err := dashboardStore.SaveDashboard(context.Background(), cmd)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dash)
|
||||
dash.Data.Set("id", dash.ID)
|
||||
dash.Data.Set("uid", dash.UID)
|
||||
|
||||
err = sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||
type folder struct {
|
||||
ID int64 `xorm:"pk autoincr 'id'"`
|
||||
OrgID int64 `xorm:"org_id"`
|
||||
UID string `xorm:"uid"`
|
||||
ParentUID *string `xorm:"parent_uid"`
|
||||
Title string `xorm:"title"`
|
||||
Description string `xorm:"description"`
|
||||
Created time.Time `xorm:"created"`
|
||||
Updated time.Time `xorm:"updated"`
|
||||
}
|
||||
|
||||
f := &folder{
|
||||
OrgID: orgId,
|
||||
UID: dash.UID,
|
||||
Title: title,
|
||||
Description: "",
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if parentUID != "" {
|
||||
f.ParentUID = &parentUID
|
||||
}
|
||||
|
||||
_, err := sess.Insert(f)
|
||||
return err
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return dash
|
||||
}
|
||||
|
||||
func TestIntegrationFindDashboardsByTitle(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
|
@ -844,13 +821,6 @@ func TestIntegrationFindDashboardsByTitle(t *testing.T) {
|
|||
orgID := int64(1)
|
||||
insertTestDashboard(t, dashboardStore, "dashboard under general", orgID, 0, "", false, []string{"tag1", "tag2"})
|
||||
|
||||
ac := acimpl.ProvideAccessControl(features)
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||
fStore := folderimpl.ProvideStore(sqlStore)
|
||||
folderServiceWithFlagOn := folderimpl.ProvideService(
|
||||
fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore,
|
||||
nil, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService(), apiserver.WithoutRestConfig)
|
||||
|
||||
user := &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
Permissions: map[int64]map[string][]string{
|
||||
|
@ -863,23 +833,10 @@ func TestIntegrationFindDashboardsByTitle(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
f0, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
Title: "f0",
|
||||
SignedInUser: user,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
f0 := insertTestFolder(t, dashboardStore, sqlStore, "f0", orgID, "")
|
||||
insertTestDashboard(t, dashboardStore, "dashboard under f0", orgID, 0, f0.UID, false, []string{"tag3"})
|
||||
|
||||
subfolder, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
Title: "subfolder",
|
||||
ParentUID: f0.UID,
|
||||
SignedInUser: user,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
subfolder := insertTestFolder(t, dashboardStore, sqlStore, "subfolder", orgID, f0.UID)
|
||||
insertTestDashboard(t, dashboardStore, "dashboard under subfolder", orgID, 0, subfolder.UID, false)
|
||||
|
||||
type res struct {
|
||||
|
@ -981,14 +938,6 @@ func TestIntegrationFindDashboardsByFolder(t *testing.T) {
|
|||
orgID := int64(1)
|
||||
insertTestDashboard(t, dashboardStore, "dashboard under general", orgID, 0, "", false)
|
||||
|
||||
ac := acimpl.ProvideAccessControl(features)
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||
fStore := folderimpl.ProvideStore(sqlStore)
|
||||
|
||||
folderServiceWithFlagOn := folderimpl.ProvideService(
|
||||
fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore,
|
||||
nil, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService(), apiserver.WithoutRestConfig)
|
||||
|
||||
user := &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
Permissions: map[int64]map[string][]string{
|
||||
|
@ -1001,31 +950,13 @@ func TestIntegrationFindDashboardsByFolder(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
f0, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
Title: "f0",
|
||||
SignedInUser: user,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// nolint:staticcheck
|
||||
insertTestDashboard(t, dashboardStore, "dashboard under f0", orgID, f0.ID, f0.UID, false)
|
||||
f0 := insertTestFolder(t, dashboardStore, sqlStore, "f0", orgID, "")
|
||||
insertTestDashboard(t, dashboardStore, "dashboard under f0", orgID, 0, f0.UID, false)
|
||||
|
||||
f1, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
Title: "f1",
|
||||
SignedInUser: user,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// nolint:staticcheck
|
||||
insertTestDashboard(t, dashboardStore, "dashboard under f1", orgID, f1.ID, f1.UID, false)
|
||||
f1 := insertTestFolder(t, dashboardStore, sqlStore, "f1", orgID, "")
|
||||
insertTestDashboard(t, dashboardStore, "dashboard under f1", orgID, 0, f1.UID, false)
|
||||
|
||||
subfolder, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
OrgID: orgID,
|
||||
Title: "subfolder",
|
||||
ParentUID: f0.UID,
|
||||
SignedInUser: user,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
subfolder := insertTestFolder(t, dashboardStore, sqlStore, "subfolder", orgID, f0.UID)
|
||||
|
||||
type res struct {
|
||||
title string
|
||||
|
|
|
@ -150,10 +150,6 @@ func (dr *DashboardServiceImpl) cleanupK8sDashboardResources(ctx context.Context
|
|||
ctx, span := tracer.Start(ctx, "dashboards.service.cleanupK8sDashboardResources")
|
||||
defer span.End()
|
||||
|
||||
if !dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return nil
|
||||
}
|
||||
|
||||
readingFromLegacy := dualwrite.IsReadingLegacyDashboardsAndFolders(ctx, dr.dual)
|
||||
if readingFromLegacy {
|
||||
// Legacy does its own cleanup
|
||||
|
@ -444,7 +440,6 @@ func (dr *DashboardServiceImpl) getPermissionsService(isFolder bool) accesscontr
|
|||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) Count(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
u := "a.Map{}
|
||||
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
||||
if err != nil {
|
||||
|
@ -478,11 +473,8 @@ func (dr *DashboardServiceImpl) Count(ctx context.Context, scopeParams *quota.Sc
|
|||
return u, nil
|
||||
}
|
||||
|
||||
return dr.dashboardStore.Count(ctx, scopeParams)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetDashboardsByLibraryPanelUID(ctx context.Context, libraryPanelUID string, orgID int64) ([]*dashboards.DashboardRef, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) && dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesLibraryPanelConnections) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesLibraryPanelConnections) {
|
||||
res, err := dr.k8sclient.Search(ctx, orgID, &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Fields: []*resourcepb.Requirement{
|
||||
|
@ -519,7 +511,6 @@ func (dr *DashboardServiceImpl) GetDashboardsByLibraryPanelUID(ctx context.Conte
|
|||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) CountDashboardsInOrg(ctx context.Context, orgID int64) (int64, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
resp, err := dr.k8sclient.GetStats(ctx, orgID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
@ -532,9 +523,6 @@ func (dr *DashboardServiceImpl) CountDashboardsInOrg(ctx context.Context, orgID
|
|||
return resp.Stats[0].Count, nil
|
||||
}
|
||||
|
||||
return dr.dashboardStore.CountInOrg(ctx, orgID, false)
|
||||
}
|
||||
|
||||
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
|
||||
limits := "a.Map{}
|
||||
|
||||
|
@ -557,7 +545,6 @@ func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
|
|||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetProvisionedDashboardData(ctx context.Context, name string) ([]*dashboards.DashboardProvisioning, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -582,11 +569,7 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardData(ctx context.Context,
|
|||
return results, nil
|
||||
}
|
||||
|
||||
return dr.dashboardStore.GetProvisionedDashboardData(ctx, name)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardID(ctx context.Context, dashboardID int64) (*dashboards.DashboardProvisioning, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
// if dashboard id is 0, it is a new dashboard
|
||||
if dashboardID == 0 {
|
||||
return nil, nil
|
||||
|
@ -617,25 +600,7 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardID(ctx con
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
data, err := dr.dashboardStore.GetProvisionedDataByDashboardID(ctx, dashboardID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &dashboards.DashboardProvisioning{
|
||||
DashboardID: data.Dashboard.ID,
|
||||
Name: data.Provisioner,
|
||||
ExternalID: data.ExternalID,
|
||||
CheckSum: data.CheckSum,
|
||||
Updated: data.ProvisionUpdate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardUID(ctx context.Context, orgID int64, dashboardUID string) (*dashboards.DashboardProvisioning, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
if dashboardUID == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -658,23 +623,6 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardUID(ctx co
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
data, err := dr.dashboardStore.GetProvisionedDataByDashboardUID(ctx, orgID, dashboardUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &dashboards.DashboardProvisioning{
|
||||
DashboardID: data.Dashboard.ID,
|
||||
Name: data.Provisioner,
|
||||
ExternalID: data.ExternalID,
|
||||
CheckSum: data.CheckSum,
|
||||
Updated: data.ProvisionUpdate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) ValidateBasicDashboardProperties(title string, uid string, message string) error {
|
||||
if title == "" {
|
||||
return dashboards.ErrDashboardTitleEmpty
|
||||
|
@ -730,7 +678,7 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
|
|||
}
|
||||
|
||||
// Validate folder
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) && (dash.FolderID != 0 || dash.FolderUID != "") { // nolint:staticcheck
|
||||
if dash.FolderID != 0 || dash.FolderUID != "" { // nolint:staticcheck
|
||||
folder, err := dr.folderService.Get(ctx, &folder.GetFolderQuery{
|
||||
OrgID: dash.OrgID,
|
||||
UID: &dash.FolderUID,
|
||||
|
@ -744,22 +692,6 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
|
|||
// nolint:staticcheck
|
||||
dash.FolderID = folder.ID
|
||||
dash.FolderUID = folder.UID
|
||||
} else if dash.FolderUID != "" {
|
||||
folder, err := dr.folderStore.GetFolderByUID(ctx, dash.OrgID, dash.FolderUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Dashboard).Inc()
|
||||
// nolint:staticcheck
|
||||
dash.FolderID = folder.ID
|
||||
} else if dash.FolderID != 0 { // nolint:staticcheck
|
||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Dashboard).Inc()
|
||||
// nolint:staticcheck
|
||||
folder, err := dr.folderStore.GetFolderByID(ctx, dash.OrgID, dash.FolderID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dash.FolderUID = folder.UID
|
||||
}
|
||||
|
||||
isParentFolderChanged, err := dr.ValidateDashboardBeforeSave(ctx, dash, dto.Overwrite)
|
||||
|
@ -944,7 +876,6 @@ func (dr *DashboardServiceImpl) waitForSearchQuery(ctx context.Context, query *d
|
|||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *dashboards.DeleteOrphanedProvisionedDashboardsCommand) error {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
// check each org for orphaned provisioned dashboards
|
||||
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
||||
if err != nil {
|
||||
|
@ -983,9 +914,6 @@ func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.
|
|||
return nil
|
||||
}
|
||||
|
||||
return dr.dashboardStore.DeleteOrphanedProvisionedDashboards(ctx, cmd)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) ValidateDashboardRefreshInterval(minRefreshInterval string, targetRefreshInterval string) error {
|
||||
if minRefreshInterval == "" {
|
||||
return nil
|
||||
|
@ -1034,13 +962,7 @@ func (dr *DashboardServiceImpl) SaveProvisionedDashboard(ctx context.Context, dt
|
|||
return nil, fmt.Errorf("failed to build save dashboard command. cmd is nil")
|
||||
}
|
||||
|
||||
var dash *dashboards.Dashboard
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
dash, err = dr.saveProvisionedDashboardThroughK8s(ctx, cmd, provisioning, false)
|
||||
} else {
|
||||
dash, err = dr.dashboardStore.SaveProvisionedDashboard(ctx, *cmd, provisioning)
|
||||
}
|
||||
|
||||
dash, err := dr.saveProvisionedDashboardThroughK8s(ctx, cmd, provisioning, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1065,10 +987,6 @@ func (dr *DashboardServiceImpl) SaveFolderForProvisionedDashboards(ctx context.C
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Only set default permissions if the Folder API Server is disabled.
|
||||
if !dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
dr.setDefaultFolderPermissions(ctx, dto, f)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
|
@ -1103,13 +1021,9 @@ func (dr *DashboardServiceImpl) SaveDashboard(ctx context.Context, dto *dashboar
|
|||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) saveDashboard(ctx context.Context, cmd *dashboards.SaveDashboardCommand) (*dashboards.Dashboard, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return dr.saveDashboardThroughK8s(ctx, cmd, cmd.OrgID)
|
||||
}
|
||||
|
||||
return dr.dashboardStore.SaveDashboard(ctx, *cmd)
|
||||
}
|
||||
|
||||
// DeleteDashboard removes dashboard from the DB. Errors out if the dashboard was provisioned. Should be used for
|
||||
// operations by the user where we want to make sure user does not delete provisioned dashboard.
|
||||
func (dr *DashboardServiceImpl) DeleteDashboard(ctx context.Context, dashboardId int64, dashboardUID string, orgId int64) error {
|
||||
|
@ -1118,13 +1032,9 @@ func (dr *DashboardServiceImpl) DeleteDashboard(ctx context.Context, dashboardId
|
|||
|
||||
// DeleteAllDashboards will delete all dashboards within a given org.
|
||||
func (dr *DashboardServiceImpl) DeleteAllDashboards(ctx context.Context, orgId int64) error {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return dr.deleteAllDashboardThroughK8s(ctx, orgId)
|
||||
}
|
||||
|
||||
return dr.dashboardStore.DeleteAllDashboards(ctx, orgId)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetDashboardByPublicUid(ctx context.Context, dashboardPublicUid string) (*dashboards.Dashboard, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -1141,30 +1051,9 @@ func (dr *DashboardServiceImpl) deleteDashboard(ctx context.Context, dashboardId
|
|||
|
||||
cmd := &dashboards.DeleteDashboardCommand{OrgID: orgId, ID: dashboardId, UID: dashboardUID}
|
||||
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return dr.deleteDashboardThroughK8s(ctx, cmd, validateProvisionedDashboard)
|
||||
}
|
||||
|
||||
if validateProvisionedDashboard {
|
||||
provisionedData, err := dr.GetProvisionedDashboardDataByDashboardID(ctx, dashboardId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v: %w", "failed to check if dashboard is provisioned", err)
|
||||
}
|
||||
|
||||
if provisionedData != nil {
|
||||
return dashboards.ErrDashboardCannotDeleteProvisionedDashboard
|
||||
}
|
||||
}
|
||||
|
||||
// deletes all related public dashboard entities
|
||||
err := dr.publicDashboardService.DeleteByDashboardUIDs(ctx, orgId, []string{dashboardUID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dr.dashboardStore.DeleteDashboard(ctx, cmd)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) ImportDashboard(ctx context.Context, dto *dashboards.SaveDashboardDTO) (
|
||||
*dashboards.Dashboard, error) {
|
||||
ctx, span := tracer.Start(ctx, "dashboards.service.ImportDashboard")
|
||||
|
@ -1195,7 +1084,6 @@ func (dr *DashboardServiceImpl) ImportDashboard(ctx context.Context, dto *dashbo
|
|||
// UnprovisionDashboard removes info about dashboard being provisioned. Used after provisioning configs are changed
|
||||
// and provisioned dashboards are left behind but not deleted.
|
||||
func (dr *DashboardServiceImpl) UnprovisionDashboard(ctx context.Context, dashboardId int64) error {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1227,11 +1115,7 @@ func (dr *DashboardServiceImpl) UnprovisionDashboard(ctx context.Context, dashbo
|
|||
return dashboards.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
return dr.dashboardStore.UnprovisionDashboard(ctx, dashboardId)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetDashboardsByPluginID(ctx context.Context, query *dashboards.GetDashboardsByPluginIDQuery) ([]*dashboards.Dashboard, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
dashs, err := dr.searchDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{
|
||||
OrgId: query.OrgID,
|
||||
ManagedBy: utils.ManagerKindPlugin,
|
||||
|
@ -1253,8 +1137,6 @@ func (dr *DashboardServiceImpl) GetDashboardsByPluginID(ctx context.Context, que
|
|||
|
||||
return results, nil
|
||||
}
|
||||
return dr.dashboardStore.GetDashboardsByPluginID(ctx, query)
|
||||
}
|
||||
|
||||
// (sometimes) called by the k8s storage engine after creating an object
|
||||
func (dr *DashboardServiceImpl) SetDefaultPermissionsAfterCreate(ctx context.Context, key *resourcepb.ResourceKey, id claims.AuthInfo, obj utils.GrafanaMetaAccessor) error {
|
||||
|
@ -1361,34 +1243,11 @@ func (dr *DashboardServiceImpl) SetDefaultPermissions(ctx context.Context, dto *
|
|||
}
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) setDefaultFolderPermissions(ctx context.Context, cmd *folder.CreateFolderCommand, f *folder.Folder) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) || !dr.cfg.RBAC.PermissionsOnCreation("folder") || f.ParentUID != "" {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, span := tracer.Start(ctx, "dashboards.service.setDefaultFolderPermissions")
|
||||
defer span.End()
|
||||
|
||||
permissions := []accesscontrol.SetResourcePermissionCommand{
|
||||
{BuiltinRole: string(org.RoleEditor), Permission: dashboardaccess.PERMISSION_EDIT.String()},
|
||||
{BuiltinRole: string(org.RoleViewer), Permission: dashboardaccess.PERMISSION_VIEW.String()},
|
||||
}
|
||||
|
||||
if _, err := dr.folderPermissions.SetPermissions(ctx, cmd.OrgID, f.UID, permissions...); err != nil {
|
||||
dr.log.Error("Could not set default folder permissions", "folder", f.Title, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetDashboard(ctx context.Context, query *dashboards.GetDashboardQuery) (*dashboards.Dashboard, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return dr.getDashboardThroughK8s(ctx, query)
|
||||
}
|
||||
|
||||
return dr.dashboardStore.GetDashboard(ctx, query)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetDashboardUIDByID(ctx context.Context, query *dashboards.GetDashboardRefByIDQuery) (*dashboards.DashboardRef, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1410,12 +1269,8 @@ func (dr *DashboardServiceImpl) GetDashboardUIDByID(ctx context.Context, query *
|
|||
return &dashboards.DashboardRef{UID: result[0].UID, Slug: result[0].Slug, FolderUID: result[0].FolderUID}, nil
|
||||
}
|
||||
|
||||
return dr.dashboardStore.GetDashboardUIDByID(ctx, query)
|
||||
}
|
||||
|
||||
// expensive query in new flow !! use sparingly - only if you truly need dashboard.Data
|
||||
func (dr *DashboardServiceImpl) GetDashboards(ctx context.Context, query *dashboards.GetDashboardsQuery) ([]*dashboards.Dashboard, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
if query.OrgID == 0 {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
|
@ -1446,9 +1301,6 @@ func (dr *DashboardServiceImpl) GetDashboards(ctx context.Context, query *dashbo
|
|||
return results, nil
|
||||
}
|
||||
|
||||
return dr.dashboardStore.GetDashboards(ctx, query)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) getDashboardsSharedWithUser(ctx context.Context, user identity.Requester) ([]*dashboards.DashboardRef, error) {
|
||||
ctx, span := tracer.Start(ctx, "dashboards.service.getDashboardsSharedWithUser")
|
||||
defer span.End()
|
||||
|
@ -1468,21 +1320,10 @@ func (dr *DashboardServiceImpl) getDashboardsSharedWithUser(ctx context.Context,
|
|||
return []*dashboards.DashboardRef{}, nil
|
||||
}
|
||||
|
||||
dashboardsQuery := &dashboards.GetDashboardsQuery{
|
||||
DashboardUIDs: dashboardUids,
|
||||
OrgID: user.GetOrgID(),
|
||||
}
|
||||
|
||||
var err error
|
||||
var dashs []*dashboards.Dashboard
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
dashs, err = dr.searchDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{
|
||||
dashs, err := dr.searchDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{
|
||||
DashboardUIDs: dashboardUids,
|
||||
OrgId: user.GetOrgID(),
|
||||
})
|
||||
} else {
|
||||
dashs, err = dr.dashboardStore.GetDashboards(ctx, dashboardsQuery)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1573,7 +1414,6 @@ func (dr *DashboardServiceImpl) FindDashboards(ctx context.Context, query *dashb
|
|||
}(time.Now())
|
||||
}
|
||||
|
||||
if dr.features.IsEnabled(ctx, featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
if query.OrgId == 0 {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
|
@ -1633,9 +1473,6 @@ func (dr *DashboardServiceImpl) FindDashboards(ctx context.Context, query *dashb
|
|||
return finalResults, nil
|
||||
}
|
||||
|
||||
return dr.dashboardStore.FindDashboards(ctx, query)
|
||||
}
|
||||
|
||||
type folderRes struct {
|
||||
Title string
|
||||
ID int64
|
||||
|
@ -1680,13 +1517,9 @@ func (dr *DashboardServiceImpl) SearchDashboards(ctx context.Context, query *das
|
|||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetAllDashboardsByOrgId(ctx context.Context, orgID int64) ([]*dashboards.Dashboard, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return dr.listDashboardsThroughK8s(ctx, orgID)
|
||||
}
|
||||
|
||||
return dr.dashboardStore.GetAllDashboardsByOrgId(ctx, orgID)
|
||||
}
|
||||
|
||||
func getHitType(item dashboards.DashboardSearchProjection) model.HitType {
|
||||
var hitType model.HitType
|
||||
if item.IsFolder {
|
||||
|
@ -1743,7 +1576,6 @@ func makeQueryResult(query *dashboards.FindPersistedDashboardsQuery, res []dashb
|
|||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetDashboardTags(ctx context.Context, query *dashboards.GetDashboardTagsQuery) ([]*dashboards.DashboardTagCloudItem, error) {
|
||||
if dr.features.IsEnabled(ctx, featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
res, err := dr.k8sclient.Search(ctx, query.OrgID, &resourcepb.ResourceSearchRequest{
|
||||
Facet: map[string]*resourcepb.ResourceSearchRequest_Facet{
|
||||
"tags": {
|
||||
|
@ -1771,11 +1603,7 @@ func (dr *DashboardServiceImpl) GetDashboardTags(ctx context.Context, query *das
|
|||
return results, nil
|
||||
}
|
||||
|
||||
return dr.dashboardStore.GetDashboardTags(ctx, query)
|
||||
}
|
||||
|
||||
func (dr DashboardServiceImpl) CountInFolders(ctx context.Context, orgID int64, folderUIDs []string, u identity.Requester) (int64, error) {
|
||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
dashs, err := dr.searchDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{
|
||||
OrgId: orgID,
|
||||
FolderUIDs: folderUIDs,
|
||||
|
@ -1787,9 +1615,6 @@ func (dr DashboardServiceImpl) CountInFolders(ctx context.Context, orgID int64,
|
|||
return int64(len(dashs)), nil
|
||||
}
|
||||
|
||||
return dr.dashboardStore.CountDashboardsInFolders(ctx, &dashboards.CountDashboardsInFolderRequest{FolderUIDs: folderUIDs, OrgID: orgID})
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) DeleteInFolders(ctx context.Context, orgID int64, folderUIDs []string, u identity.Requester) error {
|
||||
ctx, span := tracer.Start(ctx, "dashboards.service.DeleteInFolders")
|
||||
defer span.End()
|
||||
|
|
|
@ -54,25 +54,24 @@ func TestMain(m *testing.M) {
|
|||
testsuite.Run(m)
|
||||
}
|
||||
|
||||
func TestDashboardService(t *testing.T) {
|
||||
t.Run("Dashboard service tests", func(t *testing.T) {
|
||||
func TestDashboardServiceValidation(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
|
||||
folderSvc := foldertest.NewFakeService()
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
log: log.New("test.logger"),
|
||||
dashboardStore: &fakeStore,
|
||||
folderService: folderSvc,
|
||||
folderService: foldertest.NewFakeService(),
|
||||
ac: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||
features: featuremgmt.WithFeatures(),
|
||||
publicDashboardService: fakePublicDashboardService,
|
||||
}
|
||||
folderStore := foldertest.FakeFolderStore{}
|
||||
folderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(nil, dashboards.ErrFolderNotFound).Once()
|
||||
service.folderStore = &folderStore
|
||||
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||
k8sCliMock.On("GetUsersFromMeta", mock.Anything, mock.Anything).Return(map[string]*user.User{}, nil)
|
||||
|
||||
t.Run("Save dashboard validation", func(t *testing.T) {
|
||||
dto := &dashboards.SaveDashboardDTO{}
|
||||
|
@ -82,7 +81,7 @@ func TestDashboardService(t *testing.T) {
|
|||
|
||||
for _, title := range titles {
|
||||
dto.Dashboard = dashboards.NewDashboard(title)
|
||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
||||
_, err := service.SaveDashboard(ctx, dto, false)
|
||||
require.Equal(t, err, dashboards.ErrDashboardTitleEmpty)
|
||||
}
|
||||
})
|
||||
|
@ -95,8 +94,8 @@ func TestDashboardService(t *testing.T) {
|
|||
will share this with you, as a form of payment for having to read this:
|
||||
https://youtu.be/dQw4w9WgXcQ?si=KeoTIpn9tUtQnOBk! Enjoy :) Now lets see if
|
||||
this test passes or if the result is more exciting than these 500 characters
|
||||
I wrote. Best of luck to the both of us!`
|
||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
||||
I wrote. Best of luck to the both of us!!`
|
||||
_, err := service.SaveDashboard(ctx, dto, false)
|
||||
require.Equal(t, err, dashboards.ErrDashboardMessageTooLong)
|
||||
|
||||
// set to a shorter message for the rest of the tests
|
||||
|
@ -105,7 +104,7 @@ func TestDashboardService(t *testing.T) {
|
|||
|
||||
t.Run("Should return validation error if folder is named General", func(t *testing.T) {
|
||||
dto.Dashboard = dashboards.NewDashboardFolder("General")
|
||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
||||
_, err := service.SaveDashboard(ctx, dto, false)
|
||||
require.Equal(t, err, dashboards.ErrDashboardFolderNameExists)
|
||||
})
|
||||
|
||||
|
@ -129,9 +128,9 @@ func TestDashboardService(t *testing.T) {
|
|||
dto.User = &user.SignedInUser{}
|
||||
|
||||
if tc.Error == nil {
|
||||
fakeStore.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Once()
|
||||
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil).Once()
|
||||
}
|
||||
_, err := service.BuildSaveDashboardCommand(context.Background(), dto, false)
|
||||
_, err := service.BuildSaveDashboardCommand(ctx, dto, false)
|
||||
require.Equal(t, err, tc.Error)
|
||||
}
|
||||
})
|
||||
|
@ -141,137 +140,15 @@ func TestDashboardService(t *testing.T) {
|
|||
dto.Dashboard.FolderUID = "non-existing-folder"
|
||||
folderSvc := foldertest.FakeService{ExpectedError: dashboards.ErrFolderNotFound}
|
||||
service.folderService = &folderSvc
|
||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
||||
_, err := service.SaveDashboard(ctx, dto, false)
|
||||
require.Equal(t, err, dashboards.ErrFolderNotFound)
|
||||
})
|
||||
|
||||
t.Run("Should return validation error if dashboard is provisioned", func(t *testing.T) {
|
||||
fakeStore.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Once()
|
||||
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioningSearchResults{}, nil).Once()
|
||||
|
||||
dto.Dashboard = dashboards.NewDashboard("Dash")
|
||||
dto.Dashboard.SetID(3)
|
||||
dto.User = &user.SignedInUser{UserID: 1}
|
||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
||||
require.Equal(t, err, dashboards.ErrDashboardCannotSaveProvisionedDashboard)
|
||||
})
|
||||
|
||||
t.Run("Should not return validation error if dashboard is provisioned but UI updates allowed", func(t *testing.T) {
|
||||
fakeStore.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil).Once()
|
||||
fakeStore.On("SaveDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand")).Return(&dashboards.Dashboard{Data: simplejson.New()}, nil).Once()
|
||||
|
||||
dto.Dashboard = dashboards.NewDashboard("Dash")
|
||||
dto.Dashboard.SetID(3)
|
||||
dto.User = &user.SignedInUser{UserID: 1}
|
||||
_, err := service.SaveDashboard(context.Background(), dto, true)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Save provisioned dashboard validation", func(t *testing.T) {
|
||||
dto := &dashboards.SaveDashboardDTO{}
|
||||
|
||||
t.Run("Should not return validation error if dashboard is provisioned", func(t *testing.T) {
|
||||
fakeStore.On("SaveProvisionedDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand"), mock.AnythingOfType("*dashboards.DashboardProvisioning")).Return(&dashboards.Dashboard{Data: simplejson.New()}, nil).Once()
|
||||
dto.Dashboard = dashboards.NewDashboard("Dash")
|
||||
dto.Dashboard.SetID(3)
|
||||
dto.User = &user.SignedInUser{UserID: 1}
|
||||
_, err := service.SaveProvisionedDashboard(context.Background(), dto, nil)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Should override invalid refresh interval if dashboard is provisioned", func(t *testing.T) {
|
||||
fakeStore.On("SaveProvisionedDashboard", mock.Anything, mock.AnythingOfType("dashboards.SaveDashboardCommand"), mock.AnythingOfType("*dashboards.DashboardProvisioning")).Return(&dashboards.Dashboard{Data: simplejson.New()}, nil).Once()
|
||||
oldRefreshInterval := service.cfg.MinRefreshInterval
|
||||
service.cfg.MinRefreshInterval = "5m"
|
||||
defer func() { service.cfg.MinRefreshInterval = oldRefreshInterval }()
|
||||
|
||||
dto.Dashboard = dashboards.NewDashboard("Dash")
|
||||
dto.Dashboard.SetID(3)
|
||||
dto.User = &user.SignedInUser{UserID: 1}
|
||||
dto.Dashboard.Data.Set("refresh", "1s")
|
||||
_, err := service.SaveProvisionedDashboard(context.Background(), dto, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dto.Dashboard.Data.Get("refresh").MustString(), "5m")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Import dashboard validation", func(t *testing.T) {
|
||||
dto := &dashboards.SaveDashboardDTO{}
|
||||
|
||||
t.Run("Should return validation error if dashboard is provisioned", func(t *testing.T) {
|
||||
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioningSearchResults{}, nil).Once()
|
||||
|
||||
dto.Dashboard = dashboards.NewDashboard("Dash")
|
||||
dto.Dashboard.SetID(3)
|
||||
dto.User = &user.SignedInUser{UserID: 1}
|
||||
_, err := service.ImportDashboard(context.Background(), dto)
|
||||
require.Equal(t, err, dashboards.ErrDashboardCannotSaveProvisionedDashboard)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Given provisioned dashboard", func(t *testing.T) {
|
||||
t.Run("DeleteProvisionedDashboard should delete it", func(t *testing.T) {
|
||||
args := &dashboards.DeleteDashboardCommand{OrgID: 1, ID: 1}
|
||||
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
||||
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||
err := service.DeleteProvisionedDashboard(context.Background(), 1, 1)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("DeleteDashboard should fail to delete it when provisioning information is missing", func(t *testing.T) {
|
||||
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(&dashboards.DashboardProvisioningSearchResults{}, nil).Once()
|
||||
err := service.DeleteDashboard(context.Background(), 1, "", 1)
|
||||
require.Equal(t, err, dashboards.ErrDashboardCannotDeleteProvisionedDashboard)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Given non provisioned dashboard", func(t *testing.T) {
|
||||
t.Run("DeleteProvisionedDashboard should delete the dashboard", func(t *testing.T) {
|
||||
args := &dashboards.DeleteDashboardCommand{OrgID: 1, ID: 1}
|
||||
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
||||
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||
err := service.DeleteProvisionedDashboard(context.Background(), 1, 1)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("DeleteDashboard should delete it", func(t *testing.T) {
|
||||
args := &dashboards.DeleteDashboardCommand{OrgID: 1, ID: 1}
|
||||
fakeStore.On("DeleteDashboard", mock.Anything, args).Return(nil).Once()
|
||||
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.AnythingOfType("int64")).Return(nil, nil).Once()
|
||||
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||
err := service.DeleteDashboard(context.Background(), 1, "", 1)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Count dashboards in folder", func(t *testing.T) {
|
||||
fakeStore.On("CountDashboardsInFolders", mock.Anything, mock.AnythingOfType("*dashboards.CountDashboardsInFolderRequest")).Return(int64(3), nil)
|
||||
folderSvc.ExpectedFolder = &folder.Folder{UID: "i am a folder"}
|
||||
// set up a ctx with signed in user
|
||||
usr := &user.SignedInUser{UserID: 1}
|
||||
ctx := identity.WithRequester(context.Background(), usr)
|
||||
|
||||
count, err := service.CountInFolders(ctx, 1, []string{"i am a folder"}, usr)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(3), count)
|
||||
})
|
||||
|
||||
t.Run("Delete dashboards in folder", func(t *testing.T) {
|
||||
args := &dashboards.DeleteDashboardsInFolderRequest{OrgID: 1, FolderUIDs: []string{"uid"}}
|
||||
fakeStore.On("DeleteDashboardsInFolders", mock.Anything, args).Return(nil).Once()
|
||||
fakeStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil).Once()
|
||||
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||
err := service.DeleteInFolders(context.Background(), 1, []string{"uid"}, nil)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func setupK8sDashboardTests(service *DashboardServiceImpl) (context.Context, *client.MockK8sHandler) {
|
||||
mockCli := new(client.MockK8sHandler)
|
||||
service.k8sclient = mockCli
|
||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders)
|
||||
|
||||
ctx := context.Background()
|
||||
userCtx := &user.SignedInUser{UserID: 1, OrgID: 1}
|
||||
|
@ -281,27 +158,15 @@ func setupK8sDashboardTests(service *DashboardServiceImpl) (context.Context, *cl
|
|||
}
|
||||
|
||||
func TestGetDashboard(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
query := &dashboards.GetDashboardQuery{
|
||||
UID: "test-uid",
|
||||
OrgID: 1,
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("GetDashboard", mock.Anything, query).Return(&dashboards.Dashboard{}, nil).Once()
|
||||
dashboard, err := service.GetDashboard(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dashboard)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
t.Run("Should get dashboard", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
||||
"metadata": map[string]any{
|
||||
|
@ -421,23 +286,9 @@ func TestGetDashboard(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetAllDashboards(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("GetAllDashboardsByOrgId", mock.Anything, int64(1)).Return([]*dashboards.Dashboard{}, nil).Once()
|
||||
dashboard, err := service.GetAllDashboardsByOrgId(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dashboard)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
|
||||
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
||||
|
@ -469,27 +320,12 @@ func TestGetAllDashboards(t *testing.T) {
|
|||
k8sCliMock.AssertExpectations(t)
|
||||
// make sure the conversion is working
|
||||
require.True(t, reflect.DeepEqual(dashes, []*dashboards.Dashboard{&dashboardExpected}))
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetAllDashboardsByOrgId(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("GetAllDashboardsByOrgId", mock.Anything, int64(1)).Return([]*dashboards.Dashboard{}, nil).Once()
|
||||
dashboard, err := service.GetAllDashboardsByOrgId(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dashboard)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
|
||||
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
||||
|
@ -521,30 +357,16 @@ func TestGetAllDashboardsByOrgId(t *testing.T) {
|
|||
k8sCliMock.AssertExpectations(t)
|
||||
// make sure the conversion is working
|
||||
require.True(t, reflect.DeepEqual(dashes, []*dashboards.Dashboard{&dashboardExpected}))
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetProvisionedDashboardData(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
orgService: &orgtest.FakeOrgService{
|
||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("GetProvisionedDashboardData", mock.Anything, "test").Return([]*dashboards.DashboardProvisioning{}, nil).Once()
|
||||
dashboard, err := service.GetProvisionedDashboardData(context.Background(), "test")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dashboard)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled and get from relevant org", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
provisioningTimestamp := int64(1234567)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||
|
@ -634,30 +456,16 @@ func TestGetProvisionedDashboardData(t *testing.T) {
|
|||
Updated: provisioningTimestamp,
|
||||
})
|
||||
k8sCliMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetProvisionedDashboardDataByDashboardID(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
orgService: &orgtest.FakeOrgService{
|
||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, int64(1)).Return(&dashboards.DashboardProvisioningSearchResults{}, nil).Once()
|
||||
dashboard, err := service.GetProvisionedDashboardDataByDashboardID(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dashboard)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled and get from whatever org it is in", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
provisioningTimestamp := int64(1234567)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||
|
@ -736,30 +544,15 @@ func TestGetProvisionedDashboardDataByDashboardID(t *testing.T) {
|
|||
Updated: provisioningTimestamp,
|
||||
})
|
||||
k8sCliMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetProvisionedDashboardDataByDashboardUID(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
orgService: &orgtest.FakeOrgService{
|
||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("GetProvisionedDataByDashboardUID", mock.Anything, int64(1), "test").Return(&dashboards.DashboardProvisioningSearchResults{}, nil).Once()
|
||||
dashboard, err := service.GetProvisionedDashboardDataByDashboardUID(context.Background(), 1, "test")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dashboard)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
provisioningTimestamp := int64(1234567)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||
|
@ -831,16 +624,12 @@ func TestGetProvisionedDashboardDataByDashboardUID(t *testing.T) {
|
|||
Updated: provisioningTimestamp,
|
||||
})
|
||||
k8sCliMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
orgService: &orgtest.FakeOrgService{
|
||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
||||
},
|
||||
|
@ -848,19 +637,7 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
|
|||
log: log.NewNopLogger(),
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("DeleteOrphanedProvisionedDashboards", mock.Anything, &dashboards.DeleteOrphanedProvisionedDashboardsCommand{
|
||||
ReaderNames: []string{"test"},
|
||||
}).Return(nil).Once()
|
||||
err := service.DeleteOrphanedProvisionedDashboards(context.Background(), &dashboards.DeleteOrphanedProvisionedDashboardsCommand{
|
||||
ReaderNames: []string{"test"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled, delete across all orgs, but only delete file based provisioned dashboards", func(t *testing.T) {
|
||||
t.Run("Should delete across all orgs, but only delete file based provisioned dashboards", func(t *testing.T) {
|
||||
_, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||
k8sCliMock.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
@ -1008,7 +785,6 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
|
|||
repo := "test"
|
||||
singleOrgService := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
orgService: &orgtest.FakeOrgService{
|
||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}},
|
||||
},
|
||||
|
@ -1102,7 +878,6 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
|
|||
repo := "test"
|
||||
singleOrgService := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
orgService: &orgtest.FakeOrgService{
|
||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}},
|
||||
},
|
||||
|
@ -1141,15 +916,6 @@ func TestUnprovisionDashboard(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("UnprovisionDashboard", mock.Anything, int64(1)).Return(nil).Once()
|
||||
err := service.UnprovisionDashboard(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled - should remove annotations", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
dash := &unstructured.Unstructured{Object: map[string]any{
|
||||
"metadata": map[string]any{
|
||||
|
@ -1216,17 +982,12 @@ func TestUnprovisionDashboard(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
k8sCliMock.AssertExpectations(t)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetDashboardsByPluginID(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
|
||||
query := &dashboards.GetDashboardsByPluginIDQuery{
|
||||
PluginID: "testing",
|
||||
OrgID: 1,
|
||||
|
@ -1240,15 +1001,6 @@ func TestGetDashboardsByPluginID(t *testing.T) {
|
|||
},
|
||||
}}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("GetDashboardsByPluginID", mock.Anything, mock.Anything).Return([]*dashboards.Dashboard{}, nil).Once()
|
||||
_, err := service.GetDashboardsByPluginID(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||
k8sCliMock.On("Get", mock.Anything, "uid", mock.Anything, mock.Anything, mock.Anything).Return(uidUnstructured, nil)
|
||||
|
@ -1288,33 +1040,12 @@ func TestGetDashboardsByPluginID(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Len(t, dashes, 1)
|
||||
k8sCliMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetDefaultPermissionsWhenSavingFolderForProvisionedDashboards(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
|
||||
type testCase struct {
|
||||
description string
|
||||
expectedCallsToSetPermissions int
|
||||
featuresArr []any
|
||||
}
|
||||
|
||||
tcs := []testCase{
|
||||
{
|
||||
description: "folder creation succeeds, via legacy storage",
|
||||
expectedCallsToSetPermissions: 1,
|
||||
},
|
||||
{
|
||||
description: "folder creation succeeds, via API Server",
|
||||
expectedCallsToSetPermissions: 0,
|
||||
featuresArr: []any{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
folderPermService := acmock.NewMockedPermissionsService()
|
||||
folderPermService.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
|
||||
|
||||
|
@ -1345,14 +1076,12 @@ func TestSetDefaultPermissionsWhenSavingFolderForProvisionedDashboards(t *testin
|
|||
OrgID: 1,
|
||||
}
|
||||
|
||||
service.features = featuremgmt.WithFeatures(tc.featuresArr...)
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
folder, err := service.SaveFolderForProvisionedDashboards(context.Background(), cmd)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, folder)
|
||||
|
||||
folderPermService.AssertNumberOfCalls(t, "SetPermissions", tc.expectedCallsToSetPermissions)
|
||||
})
|
||||
}
|
||||
folderPermService.AssertNumberOfCalls(t, "SetPermissions", 0)
|
||||
}
|
||||
|
||||
func TestSaveProvisionedDashboard(t *testing.T) {
|
||||
|
@ -1382,17 +1111,6 @@ func TestSaveProvisionedDashboard(t *testing.T) {
|
|||
Data: simplejson.NewFromAny(map[string]any{"test": "test", "title": "testing slugify", "uid": "uid"}),
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil)
|
||||
fakeStore.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil)
|
||||
dashboard, err := service.SaveProvisionedDashboard(context.Background(), query, &dashboards.DashboardProvisioning{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dashboard)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
||||
"metadata": map[string]any{
|
||||
"name": "uid",
|
||||
|
@ -1404,9 +1122,8 @@ func TestSaveProvisionedDashboard(t *testing.T) {
|
|||
},
|
||||
}}
|
||||
|
||||
t.Run("Should use Kubernetes create if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
fakeStore.On("SaveProvisionedDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil)
|
||||
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructured, nil)
|
||||
k8sCliMock.On("GetUsersFromMeta", mock.Anything, mock.Anything).Return(map[string]*user.User{}, nil)
|
||||
k8sCliMock.On("Update", mock.Anything, mock.Anything, mock.Anything, metav1.UpdateOptions{
|
||||
FieldValidation: metav1.FieldValidationIgnore,
|
||||
|
@ -1419,7 +1136,6 @@ func TestSaveProvisionedDashboard(t *testing.T) {
|
|||
k8sCliMock.AssertExpectations(t)
|
||||
// ensure the provisioning data is still saved to the db
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSaveDashboard(t *testing.T) {
|
||||
|
@ -1446,17 +1162,6 @@ func TestSaveDashboard(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.Anything).Return(nil, nil)
|
||||
fakeStore.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil)
|
||||
fakeStore.On("SaveDashboard", mock.Anything, mock.Anything, mock.Anything).Return(&dashboards.Dashboard{}, nil)
|
||||
dashboard, err := service.SaveDashboard(context.Background(), query, false)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dashboard)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
||||
"metadata": map[string]any{
|
||||
"name": "uid",
|
||||
|
@ -1470,6 +1175,7 @@ func TestSaveDashboard(t *testing.T) {
|
|||
|
||||
t.Run("Should use Kubernetes create if feature flags are enabled and dashboard doesn't exist", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
|
||||
k8sCliMock.On("GetUsersFromMeta", mock.Anything, mock.Anything).Return(map[string]*user.User{}, nil)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything).Return("default")
|
||||
k8sCliMock.On("Update", mock.Anything, mock.Anything, mock.Anything, metav1.UpdateOptions{
|
||||
|
@ -1483,6 +1189,7 @@ func TestSaveDashboard(t *testing.T) {
|
|||
|
||||
t.Run("Should use Kubernetes update if feature flags are enabled and dashboard exists", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructured, nil)
|
||||
k8sCliMock.On("GetUsersFromMeta", mock.Anything, mock.Anything).Return(map[string]*user.User{}, nil)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything).Return("default")
|
||||
k8sCliMock.On("Update", mock.Anything, mock.Anything, mock.Anything, metav1.UpdateOptions{
|
||||
|
@ -1517,17 +1224,6 @@ func TestDeleteDashboard(t *testing.T) {
|
|||
publicDashboardService: fakePublicDashboardService,
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("DeleteDashboard", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||
fakeStore.On("GetProvisionedDataByDashboardID", mock.Anything, mock.Anything).Return(nil, nil).Once()
|
||||
fakePublicDashboardService.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||
|
||||
err := service.DeleteDashboard(context.Background(), 1, "uid", 1)
|
||||
require.NoError(t, err)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||
|
@ -1583,22 +1279,12 @@ func TestDeleteAllDashboards(t *testing.T) {
|
|||
dashboardStore: &fakeStore,
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("DeleteAllDashboards", mock.Anything, mock.Anything).Return(nil).Once()
|
||||
err := service.DeleteAllDashboards(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("DeleteCollection", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||
|
||||
err := service.DeleteAllDashboards(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
k8sCliMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSearchDashboards(t *testing.T) {
|
||||
|
@ -1612,6 +1298,7 @@ func TestSearchDashboards(t *testing.T) {
|
|||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
dashboardStore: &fakeStore,
|
||||
folderService: fakeFolders,
|
||||
metrics: newDashboardsMetrics(prometheus.NewRegistry()),
|
||||
|
@ -1651,38 +1338,8 @@ func TestSearchDashboards(t *testing.T) {
|
|||
query := dashboards.FindPersistedDashboardsQuery{
|
||||
DashboardUIDs: []string{"uid1", "uid2"},
|
||||
}
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{
|
||||
{
|
||||
UID: "uid1",
|
||||
Slug: "dashboard-1",
|
||||
OrgID: 1,
|
||||
Title: "Dashboard 1",
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
FolderTitle: "testing-folder-1",
|
||||
FolderSlug: "testing-folder-1",
|
||||
FolderUID: "f1",
|
||||
FolderID: 1,
|
||||
},
|
||||
{
|
||||
UID: "uid2",
|
||||
Slug: "dashboard-2",
|
||||
OrgID: 1,
|
||||
Title: "Dashboard 2",
|
||||
FolderTitle: "testing-folder-1",
|
||||
FolderSlug: "testing-folder-1",
|
||||
FolderUID: "f1",
|
||||
FolderID: 1,
|
||||
},
|
||||
}, nil).Once()
|
||||
result, err := service.SearchDashboards(context.Background(), &query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedResult, result)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
t.Run("Should search correctly", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
expectedFolders := model.HitList{
|
||||
{
|
||||
|
@ -1745,7 +1402,7 @@ func TestSearchDashboards(t *testing.T) {
|
|||
|
||||
t.Run("Should handle Shared with me folder correctly", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders, featuremgmt.FlagKubernetesClientDashboardsFolders)
|
||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||
k8sCliMock.On("Search", mock.Anything, int64(1), mock.MatchedBy(func(req *resourcepb.ResourceSearchRequest) bool {
|
||||
if len(req.Options.Fields) == 0 {
|
||||
|
@ -1848,11 +1505,8 @@ func TestSearchDashboards(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetDashboards(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
|
||||
expectedResult := []*dashboards.Dashboard{
|
||||
|
@ -1899,25 +1553,7 @@ func TestGetDashboards(t *testing.T) {
|
|||
DashboardUIDs: []string{"uid1", "uid2"},
|
||||
OrgID: 1,
|
||||
}
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
|
||||
// by ids
|
||||
fakeStore.On("GetDashboards", mock.Anything, queryByIDs).Return(expectedResult, nil).Once()
|
||||
result, err := service.GetDashboards(context.Background(), queryByIDs)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedResult, result)
|
||||
fakeStore.AssertExpectations(t)
|
||||
|
||||
// by uids
|
||||
fakeStore.On("GetDashboards", mock.Anything, queryByUIDs).Return(expectedResult, nil).Once()
|
||||
result, err = service.GetDashboards(context.Background(), queryByUIDs)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedResult, result)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||
k8sCliMock.On("Get", mock.Anything, "uid1", mock.Anything, mock.Anything, mock.Anything).Return(uid1Unstructured, nil)
|
||||
|
@ -1972,15 +1608,11 @@ func TestGetDashboards(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, expectedResult, result)
|
||||
k8sCliMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetDashboardUIDByID(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
|
||||
expectedResult := &dashboards.DashboardRef{
|
||||
|
@ -1991,17 +1623,6 @@ func TestGetDashboardUIDByID(t *testing.T) {
|
|||
query := &dashboards.GetDashboardRefByIDQuery{
|
||||
ID: 1,
|
||||
}
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("GetDashboardUIDByID", mock.Anything, query).Return(expectedResult, nil).Once()
|
||||
|
||||
result, err := service.GetDashboardUIDByID(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedResult, result)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resourcepb.ResourceSearchResponse{
|
||||
|
@ -2035,7 +1656,6 @@ func TestGetDashboardUIDByID(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, expectedResult, result)
|
||||
k8sCliMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnstructuredToLegacyDashboard(t *testing.T) {
|
||||
|
@ -2092,11 +1712,8 @@ func TestUnstructuredToLegacyDashboard(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetDashboardTags(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
|
||||
expectedResult := []*dashboards.DashboardTagCloudItem{
|
||||
|
@ -2112,16 +1729,6 @@ func TestGetDashboardTags(t *testing.T) {
|
|||
query := &dashboards.GetDashboardTagsQuery{
|
||||
OrgID: 1,
|
||||
}
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("GetDashboardTags", mock.Anything, query).Return(expectedResult, nil).Once()
|
||||
result, err := service.GetDashboardTags(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedResult, result)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resourcepb.ResourceSearchResponse{
|
||||
Facet: map[string]*resourcepb.ResourceSearchResponse_Facet{
|
||||
|
@ -2143,16 +1750,11 @@ func TestGetDashboardTags(t *testing.T) {
|
|||
result, err := service.GetDashboardTags(ctx, query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedResult, result)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestQuotaCount(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
|
||||
orgs := []*org.OrgDTO{
|
||||
|
@ -2182,15 +1784,6 @@ func TestQuotaCount(t *testing.T) {
|
|||
query := "a.ScopeParameters{
|
||||
OrgID: 1,
|
||||
}
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("Count", mock.Anything, mock.Anything).Return(nil, nil).Once()
|
||||
_, err := service.Count(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
orgSvc := orgtest.FakeOrgService{ExpectedOrgs: orgs}
|
||||
service.orgService = &orgSvc
|
||||
|
@ -2209,17 +1802,11 @@ func TestQuotaCount(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
c, _ = result.Get(globalTag)
|
||||
require.Equal(t, c, int64(3))
|
||||
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCountDashboardsInOrg(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
count := resourcepb.ResourceStatsResponse{
|
||||
Stats: []*resourcepb.ResourceStatsResponse_Stats{
|
||||
|
@ -2229,30 +1816,18 @@ func TestCountDashboardsInOrg(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("CountInOrg", mock.Anything, mock.Anything, false).Return(int64(1), nil).Once()
|
||||
_, err := service.CountDashboardsInOrg(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("GetStats", mock.Anything, mock.Anything).Return(&count, nil).Once()
|
||||
result, err := service.CountDashboardsInOrg(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, result, int64(3))
|
||||
})
|
||||
}
|
||||
|
||||
func TestCountInFolders(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
dashboardStore: &fakeStore,
|
||||
}
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
dashs := &resourcepb.ResourceSearchResponse{
|
||||
Results: &resourcepb.ResourceTable{
|
||||
Columns: []*resourcepb.ResourceTableColumnDefinition{
|
||||
|
@ -2291,22 +1866,11 @@ func TestCountInFolders(t *testing.T) {
|
|||
TotalHits: 2,
|
||||
}
|
||||
|
||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures()
|
||||
fakeStore.On("CountDashboardsInFolders", mock.Anything, mock.Anything).Return(int64(1), nil).Once()
|
||||
_, err := service.CountInFolders(context.Background(), 1, []string{"folder1"}, &user.SignedInUser{})
|
||||
require.NoError(t, err)
|
||||
fakeStore.AssertExpectations(t)
|
||||
})
|
||||
|
||||
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(dashs, nil).Once()
|
||||
result, err := service.CountInFolders(ctx, 1, []string{"folder1"}, &user.SignedInUser{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, result, int64(2))
|
||||
})
|
||||
}
|
||||
|
||||
func TestSearchDashboardsThroughK8sRaw(t *testing.T) {
|
||||
|
@ -2654,26 +2218,18 @@ func TestIntegrationK8sDashboardCleanupJob(t *testing.T) {
|
|||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
featureEnabled bool
|
||||
readFromUnified bool
|
||||
batchSize int
|
||||
setupFunc func(*DashboardServiceImpl, context.Context, *client.MockK8sHandler)
|
||||
verifyFunc func(*testing.T, *DashboardServiceImpl, context.Context, *client.MockK8sHandler, *kvstore.FakeKVStore)
|
||||
}{
|
||||
{
|
||||
name: "Should not run cleanup when feature flag is disabled",
|
||||
featureEnabled: false,
|
||||
batchSize: 10,
|
||||
},
|
||||
{
|
||||
name: "Should not run cleanup when feature flag is enabled but we're reading from legacy",
|
||||
featureEnabled: true,
|
||||
name: "Should not run cleanup when we're reading from legacy",
|
||||
readFromUnified: false,
|
||||
batchSize: 10,
|
||||
},
|
||||
{
|
||||
name: "Should process dashboard cleanup for all orgs",
|
||||
featureEnabled: true,
|
||||
readFromUnified: true,
|
||||
batchSize: 10,
|
||||
setupFunc: func(service *DashboardServiceImpl, ctx context.Context, k8sCliMock *client.MockK8sHandler) {
|
||||
|
@ -2744,7 +2300,6 @@ func TestIntegrationK8sDashboardCleanupJob(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "Should handle pagination and batching when processing large sets of dashboards",
|
||||
featureEnabled: true,
|
||||
readFromUnified: true,
|
||||
batchSize: 3,
|
||||
setupFunc: func(service *DashboardServiceImpl, ctx context.Context, k8sCliMock *client.MockK8sHandler) {
|
||||
|
@ -2836,11 +2391,7 @@ func TestIntegrationK8sDashboardCleanupJob(t *testing.T) {
|
|||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
||||
fakeOrgService := orgtest.NewOrgServiceFake()
|
||||
|
||||
features := featuremgmt.WithFeatures()
|
||||
if tc.featureEnabled {
|
||||
features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders)
|
||||
}
|
||||
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
|
@ -2885,7 +2436,7 @@ func TestIntegrationK8sDashboardCleanupJob(t *testing.T) {
|
|||
service := &DashboardServiceImpl{
|
||||
cfg: cfg,
|
||||
log: log.New("test.logger"),
|
||||
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
serverLockService: lockService,
|
||||
}
|
||||
|
||||
|
@ -2942,7 +2493,7 @@ func TestGetDashboardsByLibraryPanelUID(t *testing.T) {
|
|||
dashboardStore: &fakeStore,
|
||||
folderService: folderSvc,
|
||||
ac: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders, featuremgmt.FlagKubernetesLibraryPanelConnections),
|
||||
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesLibraryPanelConnections),
|
||||
publicDashboardService: fakePublicDashboardService,
|
||||
k8sclient: k8sCliMock,
|
||||
}
|
||||
|
|
|
@ -62,34 +62,6 @@ func (_m *FakeDashboardStore) Count(_a0 context.Context, _a1 *quota.ScopeParamet
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// CountDashboardsInFolders provides a mock function with given fields: ctx, request
|
||||
func (_m *FakeDashboardStore) CountDashboardsInFolders(ctx context.Context, request *CountDashboardsInFolderRequest) (int64, error) {
|
||||
ret := _m.Called(ctx, request)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for CountDashboardsInFolders")
|
||||
}
|
||||
|
||||
var r0 int64
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *CountDashboardsInFolderRequest) (int64, error)); ok {
|
||||
return rf(ctx, request)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *CountDashboardsInFolderRequest) int64); ok {
|
||||
r0 = rf(ctx, request)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *CountDashboardsInFolderRequest) error); ok {
|
||||
r1 = rf(ctx, request)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CountInOrg provides a mock function with given fields: ctx, orgID, isFolder
|
||||
func (_m *FakeDashboardStore) CountInOrg(ctx context.Context, orgID int64, isFolder bool) (int64, error) {
|
||||
ret := _m.Called(ctx, orgID, isFolder)
|
||||
|
@ -118,23 +90,6 @@ func (_m *FakeDashboardStore) CountInOrg(ctx context.Context, orgID int64, isFol
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// DeleteAllDashboards provides a mock function with given fields: ctx, orgID
|
||||
func (_m *FakeDashboardStore) DeleteAllDashboards(ctx context.Context, orgID int64) error {
|
||||
ret := _m.Called(ctx, orgID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteAllDashboards")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(ctx, orgID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteDashboard provides a mock function with given fields: ctx, cmd
|
||||
func (_m *FakeDashboardStore) DeleteDashboard(ctx context.Context, cmd *DeleteDashboardCommand) error {
|
||||
|
@ -172,23 +127,6 @@ func (_m *FakeDashboardStore) DeleteDashboardsInFolders(ctx context.Context, req
|
|||
return r0
|
||||
}
|
||||
|
||||
// DeleteOrphanedProvisionedDashboards provides a mock function with given fields: ctx, cmd
|
||||
func (_m *FakeDashboardStore) DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *DeleteOrphanedProvisionedDashboardsCommand) error {
|
||||
ret := _m.Called(ctx, cmd)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteOrphanedProvisionedDashboards")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *DeleteOrphanedProvisionedDashboardsCommand) error); ok {
|
||||
r0 = rf(ctx, cmd)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// FindDashboards provides a mock function with given fields: ctx, query
|
||||
func (_m *FakeDashboardStore) FindDashboards(ctx context.Context, query *FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error) {
|
||||
|
@ -310,66 +248,6 @@ func (_m *FakeDashboardStore) GetDashboardTags(ctx context.Context, query *GetDa
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// GetDashboardUIDByID provides a mock function with given fields: ctx, query
|
||||
func (_m *FakeDashboardStore) GetDashboardUIDByID(ctx context.Context, query *GetDashboardRefByIDQuery) (*DashboardRef, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetDashboardUIDByID")
|
||||
}
|
||||
|
||||
var r0 *DashboardRef
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *GetDashboardRefByIDQuery) (*DashboardRef, error)); ok {
|
||||
return rf(ctx, query)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *GetDashboardRefByIDQuery) *DashboardRef); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*DashboardRef)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *GetDashboardRefByIDQuery) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetDashboards provides a mock function with given fields: ctx, query
|
||||
func (_m *FakeDashboardStore) GetDashboards(ctx context.Context, query *GetDashboardsQuery) ([]*Dashboard, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetDashboards")
|
||||
}
|
||||
|
||||
var r0 []*Dashboard
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *GetDashboardsQuery) ([]*Dashboard, error)); ok {
|
||||
return rf(ctx, query)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *GetDashboardsQuery) []*Dashboard); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*Dashboard)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *GetDashboardsQuery) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetDashboardsByLibraryPanelUID provides a mock function with given fields: ctx, libraryPanelUID, orgID
|
||||
func (_m *FakeDashboardStore) GetDashboardsByLibraryPanelUID(ctx context.Context, libraryPanelUID string, orgID int64) ([]*DashboardRef, error) {
|
||||
ret := _m.Called(ctx, libraryPanelUID, orgID)
|
||||
|
|
|
@ -4,33 +4,18 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||
"github.com/grafana/grafana/pkg/infra/serverlock"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/client"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashdb "github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
dashsvc "github.com/grafana/grafana/pkg/services/dashboards/service"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
dashsnapdb "github.com/grafana/grafana/pkg/services/dashboardsnapshots/database"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/search/sort"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/database"
|
||||
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
)
|
||||
|
||||
|
@ -81,7 +66,6 @@ func TestIntegrationDashboardSnapshotsService(t *testing.T) {
|
|||
|
||||
require.Equal(t, rawDashboard, decrypted)
|
||||
})
|
||||
|
||||
t.Run("get dashboard snapshot should return the dashboard decrypted", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
|
@ -99,69 +83,3 @@ func TestIntegrationDashboardSnapshotsService(t *testing.T) {
|
|||
require.Equal(t, rawDashboard, decrypted)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntegrationValidateDashboardExists(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test in short mode")
|
||||
}
|
||||
sqlStore := db.InitTestDB(t)
|
||||
cfg := setting.NewCfg()
|
||||
dsStore := dashsnapdb.ProvideStore(sqlStore, cfg)
|
||||
secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore))
|
||||
feats := featuremgmt.WithFeatures()
|
||||
dashboardStore, err := dashdb.ProvideDashboardStore(sqlStore, cfg, feats, tagimpl.ProvideService(sqlStore))
|
||||
require.NoError(t, err)
|
||||
dashSvc, err := dashsvc.ProvideDashboardServiceImpl(
|
||||
cfg,
|
||||
dashboardStore,
|
||||
folderimpl.ProvideDashboardFolderStore(sqlStore),
|
||||
feats,
|
||||
nil,
|
||||
actest.FakeAccessControl{},
|
||||
actest.FakeService{},
|
||||
foldertest.NewFakeService(),
|
||||
nil,
|
||||
client.MockTestRestConfig{},
|
||||
nil,
|
||||
quotatest.New(false, nil),
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
dualwrite.ProvideTestService(),
|
||||
sort.ProvideService(),
|
||||
serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()),
|
||||
kvstore.NewFakeKVStore(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
s := ProvideService(dsStore, secretsService, dashSvc)
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("returns false when dashboard does not exist", func(t *testing.T) {
|
||||
err := s.ValidateDashboardExists(ctx, 1, "test")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, dashboards.ErrDashboardNotFound, err)
|
||||
})
|
||||
|
||||
t.Run("returns true when dashboard exists", func(t *testing.T) {
|
||||
err := createDashboard(sqlStore)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.ValidateDashboardExists(ctx, 1, "test")
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func createDashboard(store db.DB) error {
|
||||
return store.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||
dashboard := &dashboards.Dashboard{
|
||||
ID: 1,
|
||||
UID: "test",
|
||||
OrgID: 1,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
_, err := sess.Insert(dashboard)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package dashboardsnapshots
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
func TestCreateDashboardSnapshot_DashboardNotFound(t *testing.T) {
|
||||
mockService := &MockService{}
|
||||
cfg := dashboardsnapshot.SnapshotSharingOptions{
|
||||
SnapshotsEnabled: true,
|
||||
ExternalEnabled: false,
|
||||
}
|
||||
testUser := &user.SignedInUser{
|
||||
UserID: 1,
|
||||
OrgID: 1,
|
||||
Login: "testuser",
|
||||
Name: "Test User",
|
||||
Email: "test@example.com",
|
||||
}
|
||||
dashboard := &common.Unstructured{}
|
||||
dashboardData := map[string]interface{}{
|
||||
"uid": "test-dashboard-uid",
|
||||
"id": 123,
|
||||
}
|
||||
dashboardBytes, _ := json.Marshal(dashboardData)
|
||||
_ = json.Unmarshal(dashboardBytes, dashboard)
|
||||
|
||||
cmd := CreateDashboardSnapshotCommand{
|
||||
DashboardCreateCommand: dashboardsnapshot.DashboardCreateCommand{
|
||||
Dashboard: dashboard,
|
||||
Name: "Test Snapshot",
|
||||
},
|
||||
}
|
||||
|
||||
mockService.On("ValidateDashboardExists", mock.Anything, int64(1), "test-dashboard-uid").
|
||||
Return(dashboards.ErrDashboardNotFound)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/api/snapshots", nil)
|
||||
req = req.WithContext(identity.WithRequester(req.Context(), testUser))
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := &contextmodel.ReqContext{
|
||||
Context: &web.Context{
|
||||
Req: req,
|
||||
Resp: web.NewResponseWriter("POST", recorder),
|
||||
},
|
||||
SignedInUser: testUser,
|
||||
Logger: log.NewNopLogger(),
|
||||
}
|
||||
|
||||
CreateDashboardSnapshot(ctx, cfg, cmd, mockService)
|
||||
|
||||
mockService.AssertExpectations(t)
|
||||
assert.Equal(t, http.StatusBadRequest, recorder.Code)
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(recorder.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Dashboard not found", response["message"])
|
||||
}
|
|
@ -89,7 +89,6 @@ func (s *Service) Get(ctx context.Context, query *dashver.GetDashboardVersionQue
|
|||
query.DashboardID = id
|
||||
}
|
||||
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
version, err := s.getHistoryThroughK8s(ctx, query.OrgID, query.DashboardUID, query.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -97,14 +96,6 @@ func (s *Service) Get(ctx context.Context, query *dashver.GetDashboardVersionQue
|
|||
return version, nil
|
||||
}
|
||||
|
||||
version, err := s.store.Get(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version.Data.Set("id", version.DashboardID)
|
||||
return version.ToDTO(query.DashboardUID), nil
|
||||
}
|
||||
|
||||
func (s *Service) DeleteExpired(ctx context.Context, cmd *dashver.DeleteExpiredVersionsCommand) error {
|
||||
versionsToKeep := s.cfg.DashboardVersionsToKeep
|
||||
if versionsToKeep < 1 {
|
||||
|
@ -160,7 +151,6 @@ func (s *Service) List(ctx context.Context, query *dashver.ListDashboardVersions
|
|||
query.Limit = 1000
|
||||
}
|
||||
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
versions, err := s.listHistoryThroughK8s(
|
||||
ctx,
|
||||
query.OrgID,
|
||||
|
@ -174,19 +164,6 @@ func (s *Service) List(ctx context.Context, query *dashver.ListDashboardVersions
|
|||
return versions, nil
|
||||
}
|
||||
|
||||
dvs, err := s.store.List(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dtos := make([]*dashver.DashboardVersionDTO, len(dvs))
|
||||
for i, v := range dvs {
|
||||
dtos[i] = v.ToDTO(query.DashboardUID)
|
||||
}
|
||||
return &dashver.DashboardVersionResponse{
|
||||
Versions: dtos,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getDashUIDMaybeEmpty is a helper function which takes a dashboardID and
|
||||
// returns the UID. If the dashboard is not found, it will return an empty
|
||||
// string.
|
||||
|
|
|
@ -15,7 +15,6 @@ import (
|
|||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/client"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
|
@ -25,29 +24,12 @@ import (
|
|||
)
|
||||
|
||||
func TestDashboardVersionService(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
||||
|
||||
t.Run("Get dashboard version", func(t *testing.T) {
|
||||
dashboard := &dashver.DashboardVersion{
|
||||
ID: 11,
|
||||
Data: &simplejson.Json{},
|
||||
}
|
||||
dashboardVersionStore.ExpectedDashboardVersion = dashboard
|
||||
dashboardService.On("GetDashboardUIDByID", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).Return(&dashboards.DashboardRef{UID: "uid"}, nil)
|
||||
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(&dashboards.Dashboard{ID: 42}, nil)
|
||||
dashboardVersion, err := dashboardVersionService.Get(context.Background(), &dashver.GetDashboardVersionQuery{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dashboard.ToDTO("uid"), dashboardVersion)
|
||||
})
|
||||
|
||||
t.Run("Get dashboard versions through k8s", func(t *testing.T) {
|
||||
t.Run("Get dashboard versions", func(t *testing.T) {
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
||||
mockCli := new(client.MockK8sHandler)
|
||||
dashboardVersionService.k8sclient = mockCli
|
||||
dashboardVersionService.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders)
|
||||
dashboardVersionService.features = featuremgmt.WithFeatures()
|
||||
dashboardService.On("GetDashboardUIDByID", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).Return(&dashboards.DashboardRef{UID: "uid"}, nil)
|
||||
|
||||
creationTimestamp := time.Now().Add(time.Hour * -24).UTC()
|
||||
|
@ -133,7 +115,7 @@ func TestDashboardVersionService(t *testing.T) {
|
|||
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
||||
mockCli := new(client.MockK8sHandler)
|
||||
dashboardVersionService.k8sclient = mockCli
|
||||
dashboardVersionService.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders)
|
||||
dashboardVersionService.features = featuremgmt.WithFeatures()
|
||||
dashboardService.On("GetDashboardUIDByID", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).Return(&dashboards.DashboardRef{UID: "uid"}, nil)
|
||||
mockCli.On("List", mock.Anything, int64(1), mock.Anything).Return(nil, apierrors.NewNotFound(schema.GroupResource{Group: "dashboards.dashboard.grafana.app", Resource: "dashboard"}, "uid"))
|
||||
|
||||
|
@ -176,93 +158,12 @@ func TestDeleteExpiredVersions(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestListDashboardVersions(t *testing.T) {
|
||||
t.Run("List all versions for a given Dashboard ID", func(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
||||
dashboardVersionStore.ExpectedListVersions = []*dashver.DashboardVersion{
|
||||
{ID: 1, DashboardID: 42},
|
||||
}
|
||||
dashboardService.On("GetDashboardUIDByID", mock.Anything,
|
||||
mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).
|
||||
Return(&dashboards.DashboardRef{UID: "uid"}, nil)
|
||||
|
||||
query := dashver.ListDashboardVersionsQuery{DashboardID: 42}
|
||||
res, err := dashboardVersionService.List(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(res.Versions))
|
||||
// validate that the UID was populated
|
||||
require.EqualValues(t, &dashver.DashboardVersionResponse{Versions: []*dashver.DashboardVersionDTO{{ID: 1, DashboardID: 42, DashboardUID: "uid"}}}, res)
|
||||
})
|
||||
|
||||
t.Run("List all versions for a non-existent DashboardID", func(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService, log: log.NewNopLogger(), features: featuremgmt.WithFeatures()}
|
||||
dashboardVersionStore.ExpectedListVersions = []*dashver.DashboardVersion{
|
||||
{ID: 1, DashboardID: 42},
|
||||
}
|
||||
dashboardService.On("GetDashboardUIDByID", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).
|
||||
Return(nil, dashboards.ErrDashboardNotFound).Once()
|
||||
|
||||
query := dashver.ListDashboardVersionsQuery{DashboardID: 42}
|
||||
res, err := dashboardVersionService.List(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(res.Versions))
|
||||
// The DashboardID remains populated with the given value, even though the dash was not found
|
||||
require.EqualValues(t, &dashver.DashboardVersionResponse{Versions: []*dashver.DashboardVersionDTO{{ID: 1, DashboardID: 42}}}, res)
|
||||
})
|
||||
|
||||
t.Run("List all versions for a given DashboardUID", func(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService, log: log.NewNopLogger(), features: featuremgmt.WithFeatures()}
|
||||
dashboardVersionStore.ExpectedListVersions = []*dashver.DashboardVersion{{DashboardID: 42, ID: 1}}
|
||||
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).
|
||||
Return(&dashboards.Dashboard{ID: 42}, nil)
|
||||
|
||||
query := dashver.ListDashboardVersionsQuery{DashboardUID: "uid"}
|
||||
res, err := dashboardVersionService.List(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(res.Versions))
|
||||
// validate that the dashboardID was populated from the GetDashboard method call.
|
||||
require.EqualValues(t, &dashver.DashboardVersionResponse{Versions: []*dashver.DashboardVersionDTO{{ID: 1, DashboardID: 42, DashboardUID: "uid"}}}, res)
|
||||
})
|
||||
|
||||
t.Run("List all versions for a given non-existent DashboardUID", func(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService, log: log.NewNopLogger(), features: featuremgmt.WithFeatures()}
|
||||
dashboardVersionStore.ExpectedListVersions = []*dashver.DashboardVersion{{DashboardID: 42, ID: 1}}
|
||||
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).
|
||||
Return(nil, dashboards.ErrDashboardNotFound)
|
||||
|
||||
query := dashver.ListDashboardVersionsQuery{DashboardUID: "uid"}
|
||||
res, err := dashboardVersionService.List(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(res.Versions))
|
||||
// validate that the dashboardUID & ID are populated, even though the dash was not found
|
||||
require.EqualValues(t, &dashver.DashboardVersionResponse{Versions: []*dashver.DashboardVersionDTO{{ID: 1, DashboardID: 42, DashboardUID: "uid"}}}, res)
|
||||
})
|
||||
|
||||
t.Run("List Dashboard versions - error from store", func(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService, log: log.NewNopLogger(), features: featuremgmt.WithFeatures()}
|
||||
dashboardVersionStore.ExpectedError = dashver.ErrDashboardVersionNotFound
|
||||
|
||||
query := dashver.ListDashboardVersionsQuery{DashboardID: 42, DashboardUID: "42"}
|
||||
res, err := dashboardVersionService.List(context.Background(), &query)
|
||||
require.Nil(t, res)
|
||||
require.ErrorIs(t, err, dashver.ErrDashboardVersionNotFound)
|
||||
})
|
||||
|
||||
t.Run("List all versions for a given Dashboard ID through k8s", func(t *testing.T) {
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
||||
mockCli := new(client.MockK8sHandler)
|
||||
dashboardVersionService.k8sclient = mockCli
|
||||
dashboardVersionService.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders)
|
||||
dashboardVersionService.features = featuremgmt.WithFeatures()
|
||||
|
||||
dashboardService.On("GetDashboardUIDByID", mock.Anything,
|
||||
mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).
|
||||
|
@ -301,7 +202,7 @@ func TestListDashboardVersions(t *testing.T) {
|
|||
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
||||
mockCli := new(client.MockK8sHandler)
|
||||
dashboardVersionService.k8sclient = mockCli
|
||||
dashboardVersionService.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders)
|
||||
dashboardVersionService.features = featuremgmt.WithFeatures()
|
||||
dashboardService.On("GetDashboardUIDByID", mock.Anything,
|
||||
mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).
|
||||
Return(&dashboards.DashboardRef{UID: "uid"}, nil)
|
||||
|
|
|
@ -483,13 +483,6 @@ var (
|
|||
Owner: grafanaAppPlatformSquad,
|
||||
FrontendOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "kubernetesClientDashboardsFolders",
|
||||
Description: "Route the folder and dashboard service requests to k8s",
|
||||
Stage: FeatureStageGeneralAvailability,
|
||||
Owner: grafanaAppPlatformSquad,
|
||||
Expression: "true", // enabled by default
|
||||
},
|
||||
{
|
||||
Name: "dashboardDisableSchemaValidationV1",
|
||||
Description: "Disable schema validation for dashboards/v1",
|
||||
|
|
|
@ -440,7 +440,6 @@ alertingConversionAPI,2025-02-12T07:13:21Z,2025-04-05T08:27:02Z,9593e51da7c05ed5
|
|||
alertingJiraIntegration,2025-02-14T12:22:04Z,,af8cab92109ecfbb54625ff25a3b90a4f92dc46a,Sonia Aguilar
|
||||
alertingRuleVersionHistoryRestore,2025-02-17T12:25:32Z,,2014d27defe5668ba07913cec6f2186c90eaaab2,Sonia Aguilar
|
||||
newShareReportDrawer,2025-02-17T19:05:46Z,,9df6412e92559f2329c2baac0025f56fc57c5bf9,Ezequiel Victorero
|
||||
kubernetesClientDashboardsFolders,2025-02-18T23:11:26Z,,3e6f40c87386984be60e391b26a7559e3469dac3,Stephanie Hingtgen
|
||||
managedDualWriter,2025-02-19T14:50:39Z,,5a40c84568485da55ccb42998c6d291d310abd56,Ryan McKinley
|
||||
rendererDisableAppPluginsPreload,2025-02-24T14:43:06Z,,608d974585c696253ac629f3c7bfc3a0043cbd49,Agnès Toulet
|
||||
assetSriChecks,2025-03-04T10:56:35Z,,bbfeb8d220cc67c329aa2b5d6ed693ae1cb54325,Jack Westbrook
|
||||
|
|
|
|
@ -62,7 +62,6 @@ kubernetesSnapshots,experimental,@grafana/grafana-app-platform-squad,false,true,
|
|||
kubernetesLibraryPanels,experimental,@grafana/grafana-app-platform-squad,false,true,false
|
||||
kubernetesLibraryPanelConnections,experimental,@grafana/grafana-app-platform-squad,false,true,false
|
||||
kubernetesDashboards,experimental,@grafana/grafana-app-platform-squad,false,false,true
|
||||
kubernetesClientDashboardsFolders,GA,@grafana/grafana-app-platform-squad,false,false,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
|
||||
|
|
|
|
@ -259,10 +259,6 @@ const (
|
|||
// Use the kubernetes API in the frontend for dashboards
|
||||
FlagKubernetesDashboards = "kubernetesDashboards"
|
||||
|
||||
// FlagKubernetesClientDashboardsFolders
|
||||
// Route the folder and dashboard service requests to k8s
|
||||
FlagKubernetesClientDashboardsFolders = "kubernetesClientDashboardsFolders"
|
||||
|
||||
// FlagDashboardDisableSchemaValidationV1
|
||||
// Disable schema validation for dashboards/v1
|
||||
FlagDashboardDisableSchemaValidationV1 = "dashboardDisableSchemaValidationV1"
|
||||
|
|
|
@ -1729,19 +1729,6 @@
|
|||
"hideFromDocs": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "kubernetesClientDashboardsFolders",
|
||||
"resourceVersion": "1753448760331",
|
||||
"creationTimestamp": "2025-02-18T23:11:26Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Route the folder and dashboard service requests to k8s",
|
||||
"stage": "GA",
|
||||
"codeowner": "@grafana/grafana-app-platform-squad",
|
||||
"expression": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "kubernetesDashboards",
|
||||
|
|
|
@ -112,7 +112,6 @@ func ProvideService(
|
|||
ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(folderStore, srv))
|
||||
ac.RegisterScopeAttributeResolver(dashboards.NewFolderUIDScopeResolver(srv))
|
||||
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
k8sHandler := client.NewK8sHandler(
|
||||
dual,
|
||||
request.GetNamespaceMapper(cfg),
|
||||
|
@ -129,9 +128,7 @@ func ProvideService(
|
|||
|
||||
srv.unifiedStore = unifiedStore
|
||||
srv.k8sclient = k8sHandler
|
||||
}
|
||||
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
dashHandler := client.NewK8sHandler(
|
||||
dual,
|
||||
request.GetNamespaceMapper(cfg),
|
||||
|
@ -144,7 +141,6 @@ func ProvideService(
|
|||
features,
|
||||
)
|
||||
srv.dashboardK8sClient = dashHandler
|
||||
}
|
||||
|
||||
return srv
|
||||
}
|
||||
|
@ -201,33 +197,23 @@ func (s *Service) DBMigration(db db.DB) {
|
|||
func (s *Service) CountFoldersInOrg(ctx context.Context, orgID int64) (int64, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.CountFoldersInOrg")
|
||||
defer span.End()
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return s.unifiedStore.CountInOrg(ctx, orgID)
|
||||
}
|
||||
|
||||
return s.store.CountInOrg(ctx, orgID)
|
||||
}
|
||||
|
||||
func (s *Service) SearchFolders(ctx context.Context, q folder.SearchFoldersQuery) (model.HitList, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.SearchFolders")
|
||||
defer span.End()
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
// TODO:
|
||||
// - implement filtering by alerting folders and k6 folders (see the dashboards store `FindDashboards` method for reference)
|
||||
// - implement fallback on search client in unistore to go to legacy store (will need to read from dashboard store)
|
||||
return s.searchFoldersFromApiServer(ctx, q)
|
||||
}
|
||||
return nil, fmt.Errorf("cannot be called on the legacy folder service")
|
||||
}
|
||||
|
||||
func (s *Service) GetFolders(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.GetFolders")
|
||||
defer span.End()
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return s.getFoldersFromApiServer(ctx, q)
|
||||
}
|
||||
return s.GetFoldersLegacy(ctx, q)
|
||||
}
|
||||
|
||||
func (s *Service) GetFoldersLegacy(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.GetFoldersLegacy")
|
||||
|
@ -286,11 +272,8 @@ func (s *Service) GetFoldersLegacy(ctx context.Context, q folder.GetFoldersQuery
|
|||
func (s *Service) Get(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.Get")
|
||||
defer span.End()
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return s.getFromApiServer(ctx, q)
|
||||
}
|
||||
return s.GetLegacy(ctx, q)
|
||||
}
|
||||
|
||||
func (s *Service) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.GetLegacy")
|
||||
|
@ -377,11 +360,8 @@ func (s *Service) setFullpath(ctx context.Context, f *folder.Folder, forceLegacy
|
|||
func (s *Service) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.GetChildren")
|
||||
defer span.End()
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return s.getChildrenFromApiServer(ctx, q)
|
||||
}
|
||||
return s.GetChildrenLegacy(ctx, q)
|
||||
}
|
||||
|
||||
func (s *Service) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.GetChildrenLegacy")
|
||||
|
@ -660,11 +640,8 @@ func (s *Service) deduplicateAvailableFolders(ctx context.Context, folders []*fo
|
|||
func (s *Service) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.GetParents")
|
||||
defer span.End()
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return s.getParentsFromApiServer(ctx, q)
|
||||
}
|
||||
return s.GetParentsLegacy(ctx, q)
|
||||
}
|
||||
|
||||
func (s *Service) GetParentsLegacy(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.GetParentsLegacy")
|
||||
|
@ -679,11 +656,8 @@ func (s *Service) GetParentsLegacy(ctx context.Context, q folder.GetParentsQuery
|
|||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return s.createOnApiServer(ctx, cmd)
|
||||
}
|
||||
return s.CreateLegacy(ctx, cmd)
|
||||
}
|
||||
|
||||
func (s *Service) CreateLegacy(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
|
||||
if cmd.SignedInUser == nil || cmd.SignedInUser.IsNil() {
|
||||
|
@ -800,11 +774,8 @@ func (s *Service) CreateLegacy(ctx context.Context, cmd *folder.CreateFolderComm
|
|||
func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.Update")
|
||||
defer span.End()
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return s.updateOnApiServer(ctx, cmd)
|
||||
}
|
||||
return s.UpdateLegacy(ctx, cmd)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateLegacy(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.UpdateLegacy")
|
||||
|
@ -943,11 +914,8 @@ func prepareForUpdate(dashFolder *dashboards.Dashboard, orgId int64, userId int6
|
|||
func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) error {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.Delete")
|
||||
defer span.End()
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return s.deleteFromApiServer(ctx, cmd)
|
||||
}
|
||||
return s.DeleteLegacy(ctx, cmd)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteLegacy(ctx context.Context, cmd *folder.DeleteFolderCommand) error {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.DeleteLegacy")
|
||||
|
@ -1067,11 +1035,8 @@ func (s *Service) legacyDelete(ctx context.Context, cmd *folder.DeleteFolderComm
|
|||
func (s *Service) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.Move")
|
||||
defer span.End()
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return s.moveOnApiServer(ctx, cmd)
|
||||
}
|
||||
return s.MoveLegacy(ctx, cmd)
|
||||
}
|
||||
|
||||
func (s *Service) MoveLegacy(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.MoveLegacy")
|
||||
|
@ -1296,13 +1261,9 @@ func (s *Service) nestedFolderDelete(ctx context.Context, cmd *folder.DeleteFold
|
|||
func (s *Service) GetDescendantCounts(ctx context.Context, q *folder.GetDescendantCountsQuery) (folder.DescendantCounts, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.GetDescendantCounts")
|
||||
defer span.End()
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
return s.getDescendantCountsFromApiServer(ctx, q)
|
||||
}
|
||||
|
||||
return s.GetDescendantCountsLegacy(ctx, q)
|
||||
}
|
||||
|
||||
func (s *Service) GetDescendantCountsLegacy(ctx context.Context, q *folder.GetDescendantCountsQuery) (folder.DescendantCounts, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "folder.GetDescendantCountsLegacy")
|
||||
defer span.End()
|
||||
|
|
|
@ -193,9 +193,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
|
|||
ExpectedUser: &user.User{},
|
||||
}
|
||||
|
||||
featuresArr := []any{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders}
|
||||
features := featuremgmt.WithFeatures(featuresArr...)
|
||||
features := featuremgmt.WithFeatures()
|
||||
|
||||
tracer := noop.NewTracerProvider().Tracer("TestIntegrationFolderServiceViaUnifiedStorage")
|
||||
dashboardStore := dashboards.NewFakeDashboardStore(t)
|
||||
|
@ -521,7 +519,7 @@ func TestSearchFoldersFromApiServer(t *testing.T) {
|
|||
tracer := noop.NewTracerProvider().Tracer("TestSearchFoldersFromApiServer")
|
||||
service := Service{
|
||||
k8sclient: fakeK8sClient,
|
||||
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
unifiedStore: folderStore,
|
||||
tracer: tracer,
|
||||
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||
|
@ -764,7 +762,7 @@ func TestGetFoldersFromApiServer(t *testing.T) {
|
|||
tracer := noop.NewTracerProvider().Tracer("TestGetFoldersFromApiServer")
|
||||
service := Service{
|
||||
k8sclient: fakeK8sClient,
|
||||
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
unifiedStore: folderStore,
|
||||
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||
tracer: tracer,
|
||||
|
@ -866,7 +864,7 @@ func TestIntegrationDeleteFoldersFromApiServer(t *testing.T) {
|
|||
publicDashboardService: publicDashboardFakeService,
|
||||
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||
registry: make(map[string]folder.RegistryService),
|
||||
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
|
||||
features: featuremgmt.WithFeatures(),
|
||||
tracer: tracer,
|
||||
}
|
||||
user := &user.SignedInUser{OrgID: 1}
|
||||
|
|
|
@ -15,16 +15,49 @@ type FakeService struct {
|
|||
ExpectedError error
|
||||
ExpectedDescendantCounts map[string]int64
|
||||
LastQuery folder.GetFoldersQuery
|
||||
foldersByUID map[string]*folder.Folder
|
||||
}
|
||||
|
||||
func NewFakeService() *FakeService {
|
||||
return &FakeService{}
|
||||
return &FakeService{
|
||||
foldersByUID: make(map[string]*folder.Folder),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FakeService) AddFolder(f *folder.Folder) {
|
||||
if s.foldersByUID == nil {
|
||||
s.foldersByUID = make(map[string]*folder.Folder)
|
||||
}
|
||||
s.foldersByUID[f.UID] = f
|
||||
s.ExpectedFolders = append(s.ExpectedFolders, f)
|
||||
}
|
||||
|
||||
func (s *FakeService) SetFolders(folders map[string]*folder.Folder) {
|
||||
s.foldersByUID = folders
|
||||
s.ExpectedFolders = make([]*folder.Folder, 0, len(folders))
|
||||
for _, f := range folders {
|
||||
s.ExpectedFolders = append(s.ExpectedFolders, f)
|
||||
}
|
||||
}
|
||||
|
||||
var _ folder.Service = (*FakeService)(nil)
|
||||
|
||||
func (s *FakeService) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
return s.ExpectedFoldersRef, s.ExpectedError
|
||||
if s.ExpectedError != nil {
|
||||
return nil, s.ExpectedError
|
||||
}
|
||||
|
||||
if s.ExpectedFoldersRef != nil {
|
||||
return s.ExpectedFoldersRef, nil
|
||||
}
|
||||
|
||||
var result []*folder.FolderReference
|
||||
for _, f := range s.ExpectedFolders {
|
||||
if f.OrgID == q.OrgID && f.ParentUID == q.UID {
|
||||
result = append(result, f.ToFolderReference())
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
func (s *FakeService) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||
return s.ExpectedFoldersRef, s.ExpectedError
|
||||
|
@ -45,6 +78,11 @@ func (s *FakeService) CreateLegacy(ctx context.Context, cmd *folder.CreateFolder
|
|||
}
|
||||
|
||||
func (s *FakeService) Get(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) {
|
||||
if q.UID != nil && s.foldersByUID != nil {
|
||||
if f, exists := s.foldersByUID[*q.UID]; exists {
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
return s.ExpectedFolder, s.ExpectedError
|
||||
}
|
||||
func (s *FakeService) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) {
|
||||
|
@ -84,7 +122,23 @@ func (s *FakeService) GetDescendantCountsLegacy(ctx context.Context, q *folder.G
|
|||
}
|
||||
|
||||
func (s *FakeService) GetFolders(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) {
|
||||
return s.ExpectedFolders, s.ExpectedError
|
||||
if s.foldersByUID != nil && len(q.UIDs) > 0 {
|
||||
var result []*folder.Folder
|
||||
for _, uid := range q.UIDs {
|
||||
if f, exists := s.foldersByUID[uid]; exists {
|
||||
result = append(result, f)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
folders := make([]*folder.Folder, 0, len(s.ExpectedFolders))
|
||||
for _, f := range s.ExpectedFolders {
|
||||
if f.OrgID == q.OrgID {
|
||||
folders = append(folders, f)
|
||||
}
|
||||
}
|
||||
return folders, s.ExpectedError
|
||||
}
|
||||
|
||||
func (s *FakeService) SearchFolders(ctx context.Context, q folder.SearchFoldersQuery) (model.HitList, error) {
|
||||
|
|
|
@ -893,7 +893,6 @@ func getFoldersWithMatchingTitles(c context.Context, l *LibraryElementService, s
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
if l.features.IsEnabled(c, featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
searchQuery := folder.SearchFoldersQuery{
|
||||
OrgID: signedInUser.GetOrgID(),
|
||||
Title: query.SearchString,
|
||||
|
@ -911,21 +910,3 @@ func getFoldersWithMatchingTitles(c context.Context, l *LibraryElementService, s
|
|||
}
|
||||
return foldersWithMatchingTitles, nil
|
||||
}
|
||||
|
||||
// Fallback to GetFolders
|
||||
fs, err := l.folderService.GetFolders(c, folder.GetFoldersQuery{
|
||||
OrgID: signedInUser.GetOrgID(),
|
||||
SignedInUser: signedInUser,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
foldersWithMatchingTitles := make([]string, 0, len(fs))
|
||||
for _, f := range fs {
|
||||
if strings.Contains(strings.ToLower(f.Title), strings.ToLower(query.SearchString)) {
|
||||
foldersWithMatchingTitles = append(foldersWithMatchingTitles, f.UID)
|
||||
}
|
||||
}
|
||||
return foldersWithMatchingTitles, nil
|
||||
}
|
||||
|
|
|
@ -46,8 +46,8 @@ func TestIntegration_CreateLibraryElement(t *testing.T) {
|
|||
},
|
||||
Version: 1,
|
||||
Meta: model.LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: "uid_for_ScenarioFolder",
|
||||
FolderName: sc.folder.Title,
|
||||
FolderUID: sc.folder.UID,
|
||||
ConnectedDashboards: 0,
|
||||
Created: sc.initialResult.Result.Meta.Created,
|
||||
Updated: sc.initialResult.Result.Meta.Updated,
|
||||
|
@ -97,8 +97,8 @@ func TestIntegration_CreateLibraryElement(t *testing.T) {
|
|||
},
|
||||
Version: 1,
|
||||
Meta: model.LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: "uid_for_ScenarioFolder",
|
||||
FolderName: sc.folder.Title,
|
||||
FolderUID: sc.folder.UID,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
|
@ -176,8 +176,8 @@ func TestIntegration_CreateLibraryElement(t *testing.T) {
|
|||
},
|
||||
Version: 1,
|
||||
Meta: model.LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderUID: "uid_for_ScenarioFolder",
|
||||
FolderName: sc.folder.Title,
|
||||
FolderUID: sc.folder.UID,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Meta.Created,
|
||||
Updated: result.Result.Meta.Updated,
|
||||
|
|
|
@ -4,9 +4,9 @@ import (
|
|||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
|
@ -25,6 +25,7 @@ func TestIntegration_DeleteLibraryElement(t *testing.T) {
|
|||
|
||||
scenarioWithPanel(t, "When an admin tries to delete a library panel that exists, it should succeed and return correct ID",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.dashboardSvc.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil)
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.deleteHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
@ -47,39 +48,14 @@ func TestIntegration_DeleteLibraryElement(t *testing.T) {
|
|||
|
||||
scenarioWithPanel(t, "When an admin tries to delete a library panel that is connected, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
dashJSON := map[string]any{
|
||||
"panels": []any{
|
||||
map[string]any{
|
||||
"id": int64(1),
|
||||
"gridPos": map[string]any{
|
||||
"h": 6,
|
||||
"w": 6,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
sc.dashboardSvc.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{
|
||||
{
|
||||
ID: 1,
|
||||
UID: "test",
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
"id": int64(2),
|
||||
"gridPos": map[string]any{
|
||||
"h": 6,
|
||||
"w": 6,
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
},
|
||||
"libraryPanel": map[string]any{
|
||||
"uid": sc.initialResult.Result.UID,
|
||||
"name": sc.initialResult.Result.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
dash := dashboards.Dashboard{
|
||||
Title: "Testing deleteHandler ",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
}, nil)
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID, sc.folder.UID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
|
@ -89,6 +65,7 @@ func TestIntegration_DeleteLibraryElement(t *testing.T) {
|
|||
|
||||
scenarioWithPanel(t, "When an admin tries to delete a library panel that is connected to a non-existent dashboard, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.dashboardSvc.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, 9999999)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -8,8 +8,10 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
searchmodel "github.com/grafana/grafana/pkg/services/search/model"
|
||||
"github.com/grafana/grafana/pkg/services/search/sort"
|
||||
)
|
||||
|
||||
|
@ -406,7 +408,14 @@ func TestIntegration_GetAllLibraryElements(t *testing.T) {
|
|||
|
||||
scenarioWithPanel(t, "When an admin tries to get all library panels and two exist and folderFilterUIDs is set to existing folders, it should succeed and the result should be correct",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolder(t, sc, "NewFolder", sc.folderSvc)
|
||||
newFolder := &folder.Folder{
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
UID: "uid_for_NewFolder",
|
||||
Title: "NewFolder",
|
||||
}
|
||||
sc.folderSvc.ExpectedFolder = newFolder
|
||||
sc.folderSvc.ExpectedFolders = []*folder.Folder{newFolder}
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel2")
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
|
@ -448,7 +457,7 @@ func TestIntegration_GetAllLibraryElements(t *testing.T) {
|
|||
},
|
||||
Version: 1,
|
||||
Meta: model.LibraryElementDTOMeta{
|
||||
FolderName: "NewFolder",
|
||||
FolderName: newFolder.Title,
|
||||
FolderUID: newFolder.UID,
|
||||
ConnectedDashboards: 0,
|
||||
Created: result.Result.Elements[0].Meta.Created,
|
||||
|
@ -1171,6 +1180,14 @@ func TestIntegration_GetAllLibraryElements(t *testing.T) {
|
|||
// Folder name search integration tests
|
||||
scenarioWithPanel(t, "When searching by folder name, it should return panels in that folder",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.folderSvc.ExpectedHitList = searchmodel.HitList{
|
||||
{
|
||||
UID: sc.folder.UID,
|
||||
Title: sc.folder.Title,
|
||||
Type: searchmodel.DashHitFolder,
|
||||
},
|
||||
}
|
||||
|
||||
// Create a panel in the existing folder
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Panel in ScenarioFolder")
|
||||
|
@ -1275,6 +1292,14 @@ func TestIntegration_GetAllLibraryElements(t *testing.T) {
|
|||
|
||||
scenarioWithPanel(t, "When searching by partial folder name, it should return panels in matching folders",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.folderSvc.ExpectedHitList = searchmodel.HitList{
|
||||
{
|
||||
UID: sc.folder.UID,
|
||||
Title: sc.folder.Title,
|
||||
Type: searchmodel.DashHitFolder,
|
||||
},
|
||||
}
|
||||
|
||||
// Create a panel in the existing folder
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Test Panel")
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
|
@ -57,7 +56,7 @@ func TestIntegration_GetLibraryElement(t *testing.T) {
|
|||
},
|
||||
Version: 1,
|
||||
Meta: model.LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderName: sc.folder.Title,
|
||||
FolderUID: sc.folder.UID,
|
||||
ConnectedDashboards: 0,
|
||||
Created: res.Result.Meta.Created,
|
||||
|
@ -120,6 +119,8 @@ func TestIntegration_GetLibraryElement(t *testing.T) {
|
|||
SignedInUser: sc.reqContext.SignedInUser,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
sc.folderSvc.ExpectedFolder = nil
|
||||
sc.folderSvc.ExpectedError = folder.ErrFolderNotFound
|
||||
err = sc.sqlStore.WithDbSession(sc.reqContext.Req.Context(), func(session *db.Session) error {
|
||||
elem, err := sc.service.GetLibraryElement(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, session, result.UID)
|
||||
require.NoError(t, err)
|
||||
|
@ -131,39 +132,7 @@ func TestIntegration_GetLibraryElement(t *testing.T) {
|
|||
|
||||
scenarioWithPanel(t, "When an admin tries to get a connected library panel, it should succeed and return correct connected dashboards",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
dashJSON := map[string]any{
|
||||
"panels": []any{
|
||||
map[string]any{
|
||||
"id": int64(1),
|
||||
"gridPos": map[string]any{
|
||||
"h": 6,
|
||||
"w": 6,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
"id": int64(2),
|
||||
"gridPos": map[string]any{
|
||||
"h": 6,
|
||||
"w": 6,
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
},
|
||||
"libraryPanel": map[string]any{
|
||||
"uid": sc.initialResult.Result.UID,
|
||||
"name": sc.initialResult.Result.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
dash := dashboards.Dashboard{
|
||||
Title: "Testing getHandler",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID, sc.folder.UID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := func(res libraryElementResult) libraryElementResult {
|
||||
|
@ -187,7 +156,7 @@ func TestIntegration_GetLibraryElement(t *testing.T) {
|
|||
},
|
||||
Version: 1,
|
||||
Meta: model.LibraryElementDTOMeta{
|
||||
FolderName: "ScenarioFolder",
|
||||
FolderName: sc.folder.Title,
|
||||
FolderUID: sc.folder.UID,
|
||||
ConnectedDashboards: 1,
|
||||
Created: res.Result.Meta.Created,
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
|
||||
|
@ -28,7 +29,14 @@ func TestIntegration_PatchLibraryElement(t *testing.T) {
|
|||
|
||||
scenarioWithPanel(t, "When an admin tries to patch a library panel that exists, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolder(t, sc, "NewFolder", sc.folderSvc)
|
||||
newFolder := &folder.Folder{
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
UID: "uid_for_NewFolder",
|
||||
Title: "NewFolder",
|
||||
}
|
||||
sc.folderSvc.ExpectedFolder = newFolder
|
||||
sc.folderSvc.ExpectedFolders = []*folder.Folder{newFolder}
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: newFolder.ID, // nolint:staticcheck
|
||||
FolderUID: &newFolder.UID,
|
||||
|
@ -95,7 +103,14 @@ func TestIntegration_PatchLibraryElement(t *testing.T) {
|
|||
|
||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with folder only, it should change folder successfully and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolder(t, sc, "NewFolder", sc.folderSvc)
|
||||
newFolder := &folder.Folder{
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
UID: "uid_for_NewFolder",
|
||||
Title: "NewFolder",
|
||||
}
|
||||
sc.folderSvc.ExpectedFolder = newFolder
|
||||
sc.folderSvc.ExpectedFolders = []*folder.Folder{newFolder}
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: newFolder.ID, // nolint:staticcheck
|
||||
FolderUID: &newFolder.UID,
|
||||
|
@ -340,26 +355,35 @@ func TestIntegration_PatchLibraryElement(t *testing.T) {
|
|||
|
||||
scenarioWithPanel(t, "When an admin tries to patch a library panel with a folder where a library panel with the same name already exists, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
newFolder := createFolder(t, sc, "NewFolder", sc.folderSvc)
|
||||
newFolder := &folder.Folder{
|
||||
ID: 2,
|
||||
OrgID: 1,
|
||||
UID: "uid_for_NewFolder",
|
||||
Title: "NewFolder",
|
||||
}
|
||||
sc.folderSvc.ExpectedFolder = newFolder
|
||||
sc.folderSvc.ExpectedFolders = []*folder.Folder{newFolder}
|
||||
|
||||
// nolint:staticcheck
|
||||
command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel")
|
||||
sc.ctx.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
var result = validateAndUnMarshalResponse(t, resp)
|
||||
sc.service.createHandler(sc.reqContext)
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: 1, // nolint:staticcheck
|
||||
FolderUID: &sc.folder.UID,
|
||||
FolderID: newFolder.ID, // nolint:staticcheck
|
||||
FolderUID: &newFolder.UID,
|
||||
Version: 1,
|
||||
Kind: int64(model.PanelElement),
|
||||
}
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result.Result.UID})
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.ctx.Req.Body = mockRequestBody(cmd)
|
||||
resp = sc.service.patchHandler(sc.reqContext)
|
||||
resp := sc.service.patchHandler(sc.reqContext)
|
||||
require.Equal(t, 400, resp.Status())
|
||||
})
|
||||
|
||||
scenarioWithPanel(t, "When an admin tries to patch a library panel in another org, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.folderSvc.ExpectedFolder = nil
|
||||
sc.folderSvc.ExpectedError = folder.ErrFolderNotFound
|
||||
cmd := model.PatchLibraryElementCommand{
|
||||
FolderID: sc.folder.ID, // nolint:staticcheck
|
||||
FolderUID: &sc.folder.UID,
|
||||
|
|
|
@ -16,26 +16,17 @@ import (
|
|||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/serverlock"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/client"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||
ngstore "github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
|
@ -43,13 +34,9 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
||||
"github.com/grafana/grafana/pkg/services/publicdashboards"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/search/sort"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
@ -67,39 +54,7 @@ func TestIntegration_DeleteLibraryPanelsInFolder(t *testing.T) {
|
|||
}
|
||||
scenarioWithPanel(t, "When an admin tries to delete a folder that contains connected library elements, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
dashJSON := map[string]any{
|
||||
"panels": []any{
|
||||
map[string]any{
|
||||
"id": int64(1),
|
||||
"gridPos": map[string]any{
|
||||
"h": 6,
|
||||
"w": 6,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
"id": int64(2),
|
||||
"gridPos": map[string]any{
|
||||
"h": 6,
|
||||
"w": 6,
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
},
|
||||
"libraryPanel": map[string]any{
|
||||
"uid": sc.initialResult.Result.UID,
|
||||
"name": sc.initialResult.Result.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
dash := dashboards.Dashboard{
|
||||
Title: "Testing DeleteLibraryElementsInFolder",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID, sc.folder.UID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.UID)
|
||||
|
@ -148,39 +103,7 @@ func TestIntegration_GetLibraryPanelConnections(t *testing.T) {
|
|||
}
|
||||
scenarioWithPanel(t, "When an admin tries to get connections of library panel, it should succeed and return correct result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
dashJSON := map[string]any{
|
||||
"panels": []any{
|
||||
map[string]any{
|
||||
"id": int64(1),
|
||||
"gridPos": map[string]any{
|
||||
"h": 6,
|
||||
"w": 6,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
"id": int64(2),
|
||||
"gridPos": map[string]any{
|
||||
"h": 6,
|
||||
"w": 6,
|
||||
"x": 6,
|
||||
"y": 0,
|
||||
},
|
||||
"libraryPanel": map[string]any{
|
||||
"uid": sc.initialResult.Result.UID,
|
||||
"name": sc.initialResult.Result.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
dash := dashboards.Dashboard{
|
||||
Title: "Testing GetLibraryPanelConnections",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID, sc.folder.UID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add a connection where the dashboard doesn't exist. Shouldn't be returned in the list
|
||||
|
@ -194,8 +117,7 @@ func TestIntegration_GetLibraryPanelConnections(t *testing.T) {
|
|||
ID: sc.initialResult.Result.ID,
|
||||
Kind: sc.initialResult.Result.Kind,
|
||||
ElementID: 1,
|
||||
ConnectionID: dashInDB.ID,
|
||||
ConnectionUID: dashInDB.UID,
|
||||
ConnectionID: 1,
|
||||
Created: res.Result[0].Created,
|
||||
CreatedBy: librarypanel.LibraryElementDTOMetaUser{
|
||||
Id: 1,
|
||||
|
@ -207,6 +129,13 @@ func TestIntegration_GetLibraryPanelConnections(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
sc.dashboardSvc.On("GetDashboardsByLibraryPanelUID", mock.Anything, mock.Anything, mock.Anything).Return([]*dashboards.DashboardRef{
|
||||
{
|
||||
ID: 1,
|
||||
UID: "",
|
||||
},
|
||||
}, nil)
|
||||
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.getConnectionsHandler(sc.reqContext)
|
||||
var result = validateAndUnMarshalConnectionResponse(t, resp)
|
||||
|
@ -239,13 +168,7 @@ func TestIntegration_GetLibraryPanelConnections(t *testing.T) {
|
|||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
dash := dashboards.Dashboard{
|
||||
Title: "Testing create element",
|
||||
Data: simplejson.NewFromAny(map[string]any{}),
|
||||
}
|
||||
// nolint:staticcheck
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash, sc.folder.ID, sc.folder.UID)
|
||||
err = sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||
err = sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, 1)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
@ -319,77 +242,26 @@ type scenarioContext struct {
|
|||
initialResult libraryElementResult
|
||||
sqlStore db.DB
|
||||
log log.Logger
|
||||
folderSvc folder.Service
|
||||
folderSvc *foldertest.FakeService
|
||||
dashboardSvc *dashboards.FakeDashboardService
|
||||
}
|
||||
|
||||
func createDashboard(t *testing.T, sqlStore db.DB, user user.SignedInUser, dash *dashboards.Dashboard, folderID int64, folderUID string) *dashboards.Dashboard {
|
||||
// nolint:staticcheck
|
||||
dash.FolderID = folderID
|
||||
dash.FolderUID = folderUID
|
||||
dashItem := &dashboards.SaveDashboardDTO{
|
||||
Dashboard: dash,
|
||||
Message: "",
|
||||
OrgID: user.OrgID,
|
||||
User: &user,
|
||||
Overwrite: false,
|
||||
}
|
||||
|
||||
features := featuremgmt.WithFeatures()
|
||||
cfg := setting.NewCfg()
|
||||
quotaService := quotatest.New(false, nil)
|
||||
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore))
|
||||
require.NoError(t, err)
|
||||
ac := actest.FakeAccessControl{ExpectedEvaluate: true}
|
||||
folderPermissions := acmock.NewMockedPermissionsService()
|
||||
folderPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
|
||||
dashboardPermissions := acmock.NewMockedPermissionsService()
|
||||
dashboardPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||
fStore := folderimpl.ProvideStore(sqlStore)
|
||||
folderSvc := folderimpl.ProvideService(
|
||||
fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore,
|
||||
nil, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService(), apiserver.WithoutRestConfig)
|
||||
_, err = folderSvc.Create(context.Background(), &folder.CreateFolderCommand{UID: folderUID, SignedInUser: &user, Title: folderUID + "-title"})
|
||||
require.NoError(t, err)
|
||||
service, err := dashboardservice.ProvideDashboardServiceImpl(
|
||||
cfg, dashboardStore, folderStore,
|
||||
features, folderPermissions, ac,
|
||||
actest.FakeService{},
|
||||
folderSvc,
|
||||
nil,
|
||||
client.MockTestRestConfig{},
|
||||
nil,
|
||||
quotaService,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
dualwrite.ProvideTestService(),
|
||||
sort.ProvideService(),
|
||||
serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()),
|
||||
kvstore.NewFakeKVStore(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
service.RegisterDashboardPermissions(dashboardPermissions)
|
||||
dashboard, err := service.SaveDashboard(context.Background(), dashItem, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
return dashboard
|
||||
}
|
||||
|
||||
func createFolder(t *testing.T, sc scenarioContext, title string, folderSvc folder.Service) *folder.Folder {
|
||||
func createFolder(t *testing.T, sc scenarioContext, title string, folderSvc *foldertest.FakeService) *folder.Folder {
|
||||
t.Helper()
|
||||
ctx := identity.WithRequester(context.Background(), &sc.user)
|
||||
folder, err := folderSvc.Create(ctx, &folder.CreateFolderCommand{
|
||||
f, err := folderSvc.Create(ctx, &folder.CreateFolderCommand{
|
||||
OrgID: sc.user.OrgID, Title: title, UID: "uid_for_" + title, SignedInUser: &sc.user,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
folderSvc.ExpectedFolder = f
|
||||
folderSvc.ExpectedFolders = append(folderSvc.ExpectedFolders, f)
|
||||
|
||||
// Set user permissions on the newly created folder so that they can interact with library elements stored in it
|
||||
sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionFoldersWrite] = append(sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionFoldersWrite], dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder.UID))
|
||||
sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionFoldersRead] = append(sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionFoldersRead], dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder.UID))
|
||||
sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionDashboardsCreate] = append(sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionDashboardsCreate], dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder.UID))
|
||||
sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionFoldersWrite] = append(sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionFoldersWrite], dashboards.ScopeFoldersProvider.GetResourceScopeUID(f.UID))
|
||||
sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionFoldersRead] = append(sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionFoldersRead], dashboards.ScopeFoldersProvider.GetResourceScopeUID(f.UID))
|
||||
sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionDashboardsCreate] = append(sc.reqContext.Permissions[sc.user.OrgID][dashboards.ActionDashboardsCreate], dashboards.ScopeFoldersProvider.GetResourceScopeUID(f.UID))
|
||||
|
||||
return folder
|
||||
return f
|
||||
}
|
||||
|
||||
func validateAndUnMarshalResponse(t *testing.T, resp response.Response) libraryElementResult {
|
||||
|
@ -465,32 +337,28 @@ func setupTestScenario(t *testing.T) scenarioContext {
|
|||
sqlStore, cfg := db.InitTestDBWithCfg(t)
|
||||
t.Cleanup(db.CleanupTestDB)
|
||||
quotaService := quotatest.New(false, nil)
|
||||
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore))
|
||||
require.NoError(t, err)
|
||||
ac := acimpl.ProvideAccessControl(features)
|
||||
folderPermissions := acmock.NewMockedPermissionsService()
|
||||
folderPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
|
||||
dashboardPermissions := acmock.NewMockedPermissionsService()
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||
fStore := folderimpl.ProvideStore(sqlStore)
|
||||
publicDash := &publicdashboards.FakePublicDashboardServiceWrapper{}
|
||||
publicDash.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
folderSvc := folderimpl.ProvideService(
|
||||
fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore,
|
||||
nil, sqlStore, features, supportbundlestest.NewFakeBundleService(), publicDash, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService(), apiserver.WithoutRestConfig)
|
||||
|
||||
folderSvc := foldertest.NewFakeService()
|
||||
f := &folder.Folder{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
UID: "uid_for_ScenarioFolder",
|
||||
Title: "ScenarioFolder",
|
||||
}
|
||||
folderSvc.ExpectedFolder = f
|
||||
folderSvc.ExpectedFolders = []*folder.Folder{f}
|
||||
|
||||
dashService := dashboards.NewFakeDashboardService(t)
|
||||
|
||||
alertStore, err := ngstore.ProvideDBStore(cfg, features, sqlStore, &foldertest.FakeService{}, &dashboards.FakeDashboardService{}, ac, bus.ProvideBus(tracing.InitializeTracerForTest()))
|
||||
require.NoError(t, err)
|
||||
err = folderSvc.RegisterService(alertStore)
|
||||
require.NoError(t, err)
|
||||
dashService, dashSvcErr := dashboardservice.ProvideDashboardServiceImpl(
|
||||
cfg, dashboardStore, folderStore,
|
||||
features, folderPermissions, ac, actest.FakeService{}, folderSvc,
|
||||
nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(),
|
||||
serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()),
|
||||
kvstore.NewFakeKVStore(),
|
||||
)
|
||||
require.NoError(t, dashSvcErr)
|
||||
dashService.RegisterDashboardPermissions(dashboardPermissions)
|
||||
service := LibraryElementService{
|
||||
Cfg: cfg,
|
||||
features: featuremgmt.WithFeatures(),
|
||||
|
@ -531,6 +399,7 @@ func setupTestScenario(t *testing.T) scenarioContext {
|
|||
SignedInUser: &usr,
|
||||
},
|
||||
folderSvc: folderSvc,
|
||||
dashboardSvc: dashService,
|
||||
}
|
||||
|
||||
sc.folder = createFolder(t, sc, "ScenarioFolder", folderSvc)
|
||||
|
|
|
@ -7,43 +7,27 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||
"github.com/grafana/grafana/pkg/infra/serverlock"
|
||||
"github.com/grafana/grafana/pkg/infra/slugify"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/client"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/search/sort"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
)
|
||||
|
||||
|
@ -93,7 +77,7 @@ func TestIntegrationConnectLibraryPanelsForDashboard(t *testing.T) {
|
|||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash)
|
||||
dashInDB := createDashboard(t, sc, &dash)
|
||||
|
||||
err := sc.service.ConnectLibraryPanelsForDashboard(sc.ctx, sc.user, dashInDB)
|
||||
require.NoError(t, err)
|
||||
|
@ -191,7 +175,7 @@ func TestIntegrationConnectLibraryPanelsForDashboard(t *testing.T) {
|
|||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash)
|
||||
dashInDB := createDashboard(t, sc, &dash)
|
||||
|
||||
err = sc.service.ConnectLibraryPanelsForDashboard(sc.ctx, sc.user, dashInDB)
|
||||
require.NoError(t, err)
|
||||
|
@ -237,7 +221,7 @@ func TestIntegrationConnectLibraryPanelsForDashboard(t *testing.T) {
|
|||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash)
|
||||
dashInDB := createDashboard(t, sc, &dash)
|
||||
|
||||
err := sc.service.ConnectLibraryPanelsForDashboard(sc.ctx, sc.user, dashInDB)
|
||||
require.EqualError(t, err, errLibraryPanelHeaderUIDMissing.Error())
|
||||
|
@ -293,7 +277,7 @@ func TestIntegrationConnectLibraryPanelsForDashboard(t *testing.T) {
|
|||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||
Data: simplejson.NewFromAny(dashJSON),
|
||||
}
|
||||
dashInDB := createDashboard(t, sc.sqlStore, sc.user, &dash)
|
||||
dashInDB := createDashboard(t, sc, &dash)
|
||||
err = sc.elementService.ConnectElementsToDashboard(sc.ctx, sc.user, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -411,7 +395,7 @@ func TestIntegrationImportLibraryPanelsForDashboard(t *testing.T) {
|
|||
element, err := sc.elementService.GetElement(sc.ctx, sc.user,
|
||||
model.GetLibraryElementCommand{UID: missingUID, FolderName: dashboards.RootFolderName})
|
||||
require.NoError(t, err)
|
||||
var expected = getExpected(t, element, missingUID, missingName, missingModel)
|
||||
var expected = getExpected(t, element, missingUID, missingName, missingModel, "Test Folder")
|
||||
var result = toLibraryElement(t, element)
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
|
@ -452,7 +436,7 @@ func TestIntegrationImportLibraryPanelsForDashboard(t *testing.T) {
|
|||
element, err := sc.elementService.GetElement(sc.ctx, sc.user,
|
||||
model.GetLibraryElementCommand{UID: existingUID, FolderName: dashboards.RootFolderName})
|
||||
require.NoError(t, err)
|
||||
var expected = getExpected(t, element, existingUID, existingName, sc.initialResult.Result.Model)
|
||||
var expected = getExpected(t, element, existingUID, existingName, sc.initialResult.Result.Model, "Test Folder")
|
||||
expected.FolderUID = sc.initialResult.Result.FolderUID
|
||||
expected.Description = sc.initialResult.Result.Description
|
||||
expected.Meta.FolderUID = sc.folder.UID
|
||||
|
@ -567,7 +551,7 @@ func TestIntegrationImportLibraryPanelsForDashboard(t *testing.T) {
|
|||
|
||||
element, err := sc.elementService.GetElement(sc.ctx, sc.user, model.GetLibraryElementCommand{UID: outsideUID, FolderName: dashboards.RootFolderName})
|
||||
require.NoError(t, err)
|
||||
expected := getExpected(t, element, outsideUID, outsideName, outsideModel)
|
||||
expected := getExpected(t, element, outsideUID, outsideName, outsideModel, "Test Folder")
|
||||
result := toLibraryElement(t, element)
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
|
@ -575,7 +559,7 @@ func TestIntegrationImportLibraryPanelsForDashboard(t *testing.T) {
|
|||
|
||||
element, err = sc.elementService.GetElement(sc.ctx, sc.user, model.GetLibraryElementCommand{UID: insideUID, FolderName: dashboards.RootFolderName})
|
||||
require.NoError(t, err)
|
||||
expected = getExpected(t, element, insideUID, insideName, insideModel)
|
||||
expected = getExpected(t, element, insideUID, insideName, insideModel, "Test Folder")
|
||||
result = toLibraryElement(t, element)
|
||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
|
@ -648,6 +632,8 @@ type scenarioContext struct {
|
|||
initialResult libraryPanelResult
|
||||
sqlStore db.DB
|
||||
lps LibraryPanelService
|
||||
mockDashboard *dashboards.FakeDashboardService
|
||||
mockFolder *foldertest.FakeService
|
||||
}
|
||||
|
||||
func toLibraryElement(t *testing.T, res model.LibraryElementDTO) libraryElement {
|
||||
|
@ -685,7 +671,7 @@ func toLibraryElement(t *testing.T, res model.LibraryElementDTO) libraryElement
|
|||
}
|
||||
}
|
||||
|
||||
func getExpected(t *testing.T, res model.LibraryElementDTO, UID string, name string, lEModel map[string]any) libraryElement {
|
||||
func getExpected(t *testing.T, res model.LibraryElementDTO, UID string, name string, lEModel map[string]any, folderName string) libraryElement {
|
||||
marshalled, err := json.Marshal(lEModel)
|
||||
require.NoError(t, err)
|
||||
var libModel libraryElementModel
|
||||
|
@ -704,7 +690,7 @@ func getExpected(t *testing.T, res model.LibraryElementDTO, UID string, name str
|
|||
Model: libModel,
|
||||
Version: 1,
|
||||
Meta: model.LibraryElementDTOMeta{
|
||||
FolderName: "General",
|
||||
FolderName: folderName,
|
||||
FolderUID: res.FolderUID,
|
||||
ConnectedDashboards: 0,
|
||||
Created: res.Meta.Created,
|
||||
|
@ -722,59 +708,14 @@ func getExpected(t *testing.T, res model.LibraryElementDTO, UID string, name str
|
|||
},
|
||||
}
|
||||
}
|
||||
func createDashboard(t *testing.T, sc scenarioContext, dash *dashboards.Dashboard) *dashboards.Dashboard {
|
||||
dash.ID = 1
|
||||
dash.UID = "test-dashboard-uid"
|
||||
dash.Created = time.Now()
|
||||
dash.Updated = time.Now()
|
||||
dash.Version = 1
|
||||
|
||||
func createDashboard(t *testing.T, sqlStore db.DB, user *user.SignedInUser, dash *dashboards.Dashboard) *dashboards.Dashboard {
|
||||
dashItem := &dashboards.SaveDashboardDTO{
|
||||
Dashboard: dash,
|
||||
Message: "",
|
||||
OrgID: user.OrgID,
|
||||
User: user,
|
||||
Overwrite: false,
|
||||
}
|
||||
|
||||
features := featuremgmt.WithFeatures()
|
||||
cfg := setting.NewCfg()
|
||||
quotaService := quotatest.New(false, nil)
|
||||
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore))
|
||||
require.NoError(t, err)
|
||||
ac := actest.FakeAccessControl{ExpectedEvaluate: true}
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||
dashPermissionService := acmock.NewMockedPermissionsService()
|
||||
dashPermissionService.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
|
||||
service, err := dashboardservice.ProvideDashboardServiceImpl(
|
||||
cfg, dashboardStore, folderStore,
|
||||
features, acmock.NewMockedPermissionsService(), ac, actest.FakeService{}, foldertest.NewFakeService(),
|
||||
nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(),
|
||||
serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()),
|
||||
kvstore.NewFakeKVStore())
|
||||
require.NoError(t, err)
|
||||
service.RegisterDashboardPermissions(dashPermissionService)
|
||||
dashboard, err := service.SaveDashboard(context.Background(), dashItem, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
return dashboard
|
||||
}
|
||||
|
||||
func createFolder(t *testing.T, sc scenarioContext, title string) *folder.Folder {
|
||||
t.Helper()
|
||||
|
||||
features := featuremgmt.WithFeatures()
|
||||
ac := actest.FakeAccessControl{ExpectedEvaluate: true}
|
||||
cfg := setting.NewCfg()
|
||||
dashboardStore, err := database.ProvideDashboardStore(sc.sqlStore, cfg, features, tagimpl.ProvideService(sc.sqlStore))
|
||||
require.NoError(t, err)
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sc.sqlStore)
|
||||
fStore := folderimpl.ProvideStore(sc.sqlStore)
|
||||
s := folderimpl.ProvideService(
|
||||
fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore,
|
||||
nil, sc.sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService(), apiserver.WithoutRestConfig)
|
||||
|
||||
t.Logf("Creating folder with title and UID %q", title)
|
||||
ctx := identity.WithRequester(context.Background(), sc.user)
|
||||
folder, err := s.Create(ctx, &folder.CreateFolderCommand{OrgID: sc.user.OrgID, Title: title, UID: title, SignedInUser: sc.user})
|
||||
require.NoError(t, err)
|
||||
|
||||
return folder
|
||||
return dash
|
||||
}
|
||||
|
||||
func scenarioWithLibraryPanel(t *testing.T, desc string, fn func(t *testing.T, sc scenarioContext)) {
|
||||
|
@ -821,8 +762,6 @@ func scenarioWithLibraryPanel(t *testing.T, desc string, fn func(t *testing.T, s
|
|||
})
|
||||
}
|
||||
|
||||
// testScenario is a wrapper around t.Run performing common setup for library panel tests.
|
||||
// It takes your real test function as a callback.
|
||||
func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioContext)) {
|
||||
t.Helper()
|
||||
|
||||
|
@ -832,38 +771,29 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||
sqlStore, cfg := db.InitTestDBWithCfg(t)
|
||||
quotaService := quotatest.New(false, nil)
|
||||
features := featuremgmt.WithFeatures()
|
||||
|
||||
ac := actest.FakeAccessControl{ExpectedEvaluate: true}
|
||||
dashStore := &dashboards.FakeDashboardStore{}
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||
dashPermissionService := acmock.NewMockedPermissionsService()
|
||||
folderSvc := foldertest.NewFakeService()
|
||||
folderSvc.ExpectedFolder = &folder.Folder{ID: 1}
|
||||
dashService, err := dashboardservice.ProvideDashboardServiceImpl(
|
||||
cfg, dashStore, folderStore,
|
||||
features, acmock.NewMockedPermissionsService(), ac, actest.FakeService{}, folderSvc,
|
||||
nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(),
|
||||
serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()),
|
||||
kvstore.NewFakeKVStore())
|
||||
require.NoError(t, err)
|
||||
dashService.RegisterDashboardPermissions(dashPermissionService)
|
||||
|
||||
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore))
|
||||
require.NoError(t, err)
|
||||
fStore := folderimpl.ProvideStore(sqlStore)
|
||||
|
||||
folderService := folderimpl.ProvideService(
|
||||
fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore,
|
||||
nil, sqlStore, features, supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService(), apiserver.WithoutRestConfig)
|
||||
|
||||
elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), folderService, features, ac, dashService, nil, nil)
|
||||
mockDashboardService := dashboards.NewFakeDashboardService(t)
|
||||
mockFolderService := foldertest.NewFakeService()
|
||||
mockFolder := &folder.Folder{
|
||||
ID: 1,
|
||||
UID: "test-folder-uid",
|
||||
Title: "Test Folder",
|
||||
URL: "/dashboards/f/test-folder-uid/test-folder",
|
||||
Version: 0,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
UpdatedBy: 0,
|
||||
CreatedBy: 0,
|
||||
HasACL: false,
|
||||
}
|
||||
mockFolderService.ExpectedFolder = mockFolder
|
||||
elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), mockFolderService, features, ac, mockDashboardService, nil, nil)
|
||||
service := LibraryPanelService{
|
||||
Cfg: cfg,
|
||||
SQLStore: sqlStore,
|
||||
LibraryElementService: elementService,
|
||||
FolderService: folderService,
|
||||
FolderService: mockFolderService,
|
||||
}
|
||||
|
||||
usr := &user.SignedInUser{
|
||||
UserID: 1,
|
||||
Name: "Signed In User",
|
||||
|
@ -872,17 +802,12 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||
OrgID: orgID,
|
||||
OrgRole: role,
|
||||
LastSeenAt: time.Now(),
|
||||
// Allow the user to create folders
|
||||
Permissions: map[int64]map[string][]string{
|
||||
orgID: {
|
||||
dashboards.ActionFoldersRead: {dashboards.ScopeFoldersAll},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// deliberate difference between signed in user and user in db to make it crystal clear
|
||||
// what to expect in the tests
|
||||
// In the real world these are identical
|
||||
cmd := user.CreateUserCommand{
|
||||
Email: "user.in.db@test.com",
|
||||
Name: "User In DB",
|
||||
|
@ -898,6 +823,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||
require.NoError(t, err)
|
||||
_, err = usrSvc.Create(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
sc := scenarioContext{
|
||||
user: usr,
|
||||
ctx: ctx,
|
||||
|
@ -905,21 +831,11 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
|||
elementService: elementService,
|
||||
sqlStore: sqlStore,
|
||||
lps: service,
|
||||
mockDashboard: mockDashboardService,
|
||||
mockFolder: mockFolderService,
|
||||
}
|
||||
|
||||
foldr := createFolder(t, sc, "ScenarioFolder")
|
||||
sc.folder = &folder.Folder{
|
||||
ID: foldr.ID, // nolint:staticcheck
|
||||
UID: foldr.UID,
|
||||
Title: foldr.Title,
|
||||
URL: dashboards.GetFolderURL(foldr.UID, slugify.Slugify(foldr.Title)),
|
||||
Version: 0,
|
||||
Created: foldr.Created,
|
||||
Updated: foldr.Updated,
|
||||
UpdatedBy: 0,
|
||||
CreatedBy: 0,
|
||||
HasACL: false,
|
||||
}
|
||||
sc.folder = mockFolder
|
||||
fn(t, sc)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -571,6 +571,7 @@ func TestRouteConvertPrometheusGetRuleGroup(t *testing.T) {
|
|||
fldr.ParentUID = ""
|
||||
folderService.ExpectedFolder = fldr
|
||||
folderService.ExpectedFolders = []*folder.Folder{fldr}
|
||||
folderService.AddFolder(fldr)
|
||||
ruleStore.Folders[1] = append(ruleStore.Folders[1], fldr)
|
||||
|
||||
// Create rules in both folders
|
||||
|
@ -669,6 +670,8 @@ func TestRouteConvertPrometheusGetNamespace(t *testing.T) {
|
|||
fldr2 := randFolder()
|
||||
fldr2.ParentUID = ""
|
||||
folderService.ExpectedFolders = []*folder.Folder{fldr, fldr2}
|
||||
folderService.AddFolder(fldr)
|
||||
folderService.AddFolder(fldr2)
|
||||
ruleStore.Folders[1] = append(ruleStore.Folders[1], fldr, fldr2)
|
||||
|
||||
// Create a Grafana rule for each Prometheus rule
|
||||
|
@ -798,6 +801,7 @@ func TestRouteConvertPrometheusGetRules(t *testing.T) {
|
|||
// Create a folder in the root
|
||||
fldr := randFolder()
|
||||
fldr.ParentUID = ""
|
||||
folderService.AddFolder(fldr)
|
||||
folderService.ExpectedFolders = []*folder.Folder{fldr}
|
||||
ruleStore.Folders[1] = append(ruleStore.Folders[1], fldr)
|
||||
|
||||
|
|
|
@ -29,14 +29,10 @@ import (
|
|||
"github.com/grafana/grafana/pkg/infra/log/logtest"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol/fakes"
|
||||
|
@ -47,14 +43,10 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
ngalertfakes "github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/search/sort"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
secrets_fakes "github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
|
@ -353,14 +345,6 @@ func TestIntegrationProvisioningApi(t *testing.T) {
|
|||
orgID := int64(2)
|
||||
|
||||
rule := createTestAlertRule("rule", orgID)
|
||||
_, err := sut.folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
UID: rule.FolderUID,
|
||||
Title: "Folder Title",
|
||||
OrgID: orgID,
|
||||
SignedInUser: &user.SignedInUser{OrgID: orgID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
insertRuleInOrg(t, sut, rule, orgID)
|
||||
rule.FolderUID = "does-not-exist"
|
||||
|
||||
|
@ -462,14 +446,7 @@ func TestIntegrationProvisioningApi(t *testing.T) {
|
|||
rc.Req.Header = map[string][]string{"X-Disable-Provenance": {"true"}}
|
||||
rc.OrgID = 3
|
||||
rule := createTestAlertRule("rule", 1)
|
||||
|
||||
_, err := sut.folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
UID: "folder-uid",
|
||||
Title: "Folder Title",
|
||||
OrgID: rc.OrgID,
|
||||
SignedInUser: &user.SignedInUser{OrgID: rc.OrgID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
rule.FolderUID = "folder-uid3"
|
||||
|
||||
response := sut.RoutePostAlertRule(&rc, rule)
|
||||
|
||||
|
@ -486,13 +463,7 @@ func TestIntegrationProvisioningApi(t *testing.T) {
|
|||
rule.UID = uid
|
||||
|
||||
orgID := int64(3)
|
||||
_, err := sut.folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
UID: "folder-uid",
|
||||
Title: "Folder Title",
|
||||
OrgID: orgID,
|
||||
SignedInUser: &user.SignedInUser{OrgID: orgID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
rule.FolderUID = "folder-uid3"
|
||||
|
||||
insertRuleInOrg(t, sut, rule, orgID)
|
||||
rc := createTestRequestCtx()
|
||||
|
@ -560,14 +531,7 @@ func TestIntegrationProvisioningApi(t *testing.T) {
|
|||
uid := util.GenerateShortUID()
|
||||
rule := createTestAlertRule("rule", 3)
|
||||
rule.UID = uid
|
||||
|
||||
_, err := sut.folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
UID: rule.FolderUID,
|
||||
Title: "Folder Title",
|
||||
OrgID: rule.OrgID,
|
||||
SignedInUser: &user.SignedInUser{OrgID: rule.OrgID},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
rule.FolderUID = "folder-uid3"
|
||||
|
||||
insertRuleInOrg(t, sut, rule, 3)
|
||||
|
||||
|
@ -2053,7 +2017,7 @@ func createTestEnv(t *testing.T, testConfig string) testEnvironment {
|
|||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: string(raw),
|
||||
})
|
||||
sqlStore, cfg := db.InitTestDBWithCfg(t)
|
||||
sqlStore, _ := db.InitTestDBWithCfg(t)
|
||||
|
||||
quotas := &provisioning.MockQuotaChecker{}
|
||||
quotas.EXPECT().LimitOK()
|
||||
|
@ -2077,14 +2041,39 @@ func createTestEnv(t *testing.T, testConfig string) testEnvironment {
|
|||
}}, nil).Maybe()
|
||||
|
||||
ac := &recordingAccessControlFake{}
|
||||
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore))
|
||||
require.NoError(t, err)
|
||||
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||
fStore := folderimpl.ProvideStore(sqlStore)
|
||||
folderService := folderimpl.ProvideService(
|
||||
fStore, actest.FakeAccessControl{ExpectedEvaluate: true}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore,
|
||||
nil, sqlStore, featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService(), apiserver.WithoutRestConfig)
|
||||
folderService := foldertest.NewFakeService()
|
||||
folder1 := &folder.Folder{
|
||||
UID: "folder-uid",
|
||||
Title: "Folder Title",
|
||||
Fullpath: "Folder Title",
|
||||
OrgID: 1,
|
||||
}
|
||||
folder2 := &folder.Folder{
|
||||
UID: "folder-uid2",
|
||||
Title: "Folder Title2",
|
||||
ParentUID: "folder-uid",
|
||||
Fullpath: "Folder Title2",
|
||||
OrgID: 1,
|
||||
}
|
||||
folder3 := &folder.Folder{
|
||||
UID: "folder-uid3",
|
||||
Title: "Folder Title3",
|
||||
ParentUID: "folder-uid",
|
||||
Fullpath: "Folder Title3",
|
||||
OrgID: 3,
|
||||
}
|
||||
folderService.SetFolders(map[string]*folder.Folder{
|
||||
"folder-uid": folder1,
|
||||
"folder-uid2": folder2,
|
||||
"folder-uid3": folder3,
|
||||
})
|
||||
folderService.ExpectedFolders = []*folder.Folder{
|
||||
folder1,
|
||||
folder2,
|
||||
folder3,
|
||||
}
|
||||
// if not one of the two above, return ErrFolderNotFound
|
||||
folderService.ExpectedError = dashboards.ErrFolderNotFound
|
||||
store := store.DBstore{
|
||||
Logger: log,
|
||||
SQLStore: sqlStore,
|
||||
|
@ -2104,23 +2093,6 @@ func createTestEnv(t *testing.T, testConfig string) testEnvironment {
|
|||
*/
|
||||
}
|
||||
|
||||
parent, err := folderService.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
OrgID: 1,
|
||||
UID: "folder-uid",
|
||||
Title: "Folder Title",
|
||||
SignedInUser: user,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = folderService.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
OrgID: 1,
|
||||
UID: "folder-uid2",
|
||||
Title: "Folder Title2",
|
||||
ParentUID: parent.UID,
|
||||
SignedInUser: user,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
ruleAuthz := &fakes.FakeRuleService{}
|
||||
|
||||
features := featuremgmt.WithFeatures()
|
||||
|
|
|
@ -27,12 +27,12 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/testutil"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
|
@ -242,10 +242,10 @@ func TestIntegration_GetAlertRulesForScheduling(t *testing.T) {
|
|||
}
|
||||
|
||||
sqlStore := db.InitTestDB(t)
|
||||
folderService := setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures())
|
||||
fakeFolderService := foldertest.NewFakeService()
|
||||
b := &fakeBus{}
|
||||
logger := &logtest.Fake{}
|
||||
store := createTestStore(sqlStore, folderService, logger, cfg.UnifiedAlerting, b)
|
||||
store := createTestStore(sqlStore, fakeFolderService, logger, cfg.UnifiedAlerting, b)
|
||||
store.FeatureToggles = featuremgmt.WithFeatures()
|
||||
|
||||
gen := models.RuleGen
|
||||
|
@ -258,15 +258,38 @@ func TestIntegration_GetAlertRulesForScheduling(t *testing.T) {
|
|||
|
||||
parentFolderUid := uuid.NewString()
|
||||
parentFolderTitle := "Very Parent Folder"
|
||||
createFolder(t, store, parentFolderUid, parentFolderTitle, rule1.OrgID, "")
|
||||
rule1FolderTitle := "folder-" + rule1.Title
|
||||
rule2FolderTitle := "folder-" + rule2.Title
|
||||
rule3FolderTitle := "folder-" + rule3.Title
|
||||
createFolder(t, store, rule1.NamespaceUID, rule1FolderTitle, rule1.OrgID, parentFolderUid)
|
||||
createFolder(t, store, rule2.NamespaceUID, rule2FolderTitle, rule2.OrgID, "")
|
||||
createFolder(t, store, rule3.NamespaceUID, rule3FolderTitle, rule3.OrgID, "")
|
||||
|
||||
createFolder(t, store, rule2.NamespaceUID, "same UID folder", gen.GenerateRef().OrgID, "") // create a folder with the same UID but in the different org
|
||||
fakeFolderService.AddFolder(&folder.Folder{
|
||||
UID: rule1.NamespaceUID,
|
||||
Title: rule1FolderTitle,
|
||||
OrgID: rule1.OrgID,
|
||||
ParentUID: parentFolderUid,
|
||||
Fullpath: rule1FolderTitle,
|
||||
})
|
||||
fakeFolderService.AddFolder(&folder.Folder{
|
||||
UID: rule2.NamespaceUID,
|
||||
Title: rule2FolderTitle,
|
||||
OrgID: rule2.OrgID,
|
||||
ParentUID: "",
|
||||
Fullpath: rule2FolderTitle,
|
||||
})
|
||||
fakeFolderService.AddFolder(&folder.Folder{
|
||||
UID: rule3.NamespaceUID,
|
||||
Title: rule3FolderTitle,
|
||||
OrgID: rule3.OrgID,
|
||||
ParentUID: "",
|
||||
Fullpath: rule3FolderTitle,
|
||||
})
|
||||
fakeFolderService.AddFolder(&folder.Folder{
|
||||
UID: parentFolderUid,
|
||||
Title: parentFolderTitle,
|
||||
OrgID: rule1.OrgID,
|
||||
ParentUID: "",
|
||||
Fullpath: parentFolderTitle,
|
||||
})
|
||||
|
||||
tc := []struct {
|
||||
name string
|
||||
|
@ -343,7 +366,14 @@ func TestIntegration_GetAlertRulesForScheduling(t *testing.T) {
|
|||
}
|
||||
|
||||
t.Run("when nested folders are enabled folders should contain full path", func(t *testing.T) {
|
||||
store.FolderService = setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders))
|
||||
fakeFolderService.AddFolder(&folder.Folder{
|
||||
UID: rule1.NamespaceUID,
|
||||
Title: rule1FolderTitle,
|
||||
OrgID: rule1.OrgID,
|
||||
ParentUID: parentFolderUid,
|
||||
Fullpath: parentFolderTitle + "/" + rule1FolderTitle,
|
||||
})
|
||||
|
||||
query := &models.GetAlertRulesForSchedulingQuery{
|
||||
PopulateFolders: true,
|
||||
}
|
||||
|
@ -1597,27 +1627,6 @@ func createRule(t *testing.T, store *DBstore, generator *models.AlertRuleGenerat
|
|||
return rule
|
||||
}
|
||||
|
||||
func createFolder(t *testing.T, store *DBstore, uid, title string, orgID int64, parentUID string) {
|
||||
t.Helper()
|
||||
u := &user.SignedInUser{
|
||||
UserID: 1,
|
||||
OrgID: orgID,
|
||||
OrgRole: org.RoleAdmin,
|
||||
IsGrafanaAdmin: true,
|
||||
}
|
||||
|
||||
_, err := store.FolderService.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
UID: uid,
|
||||
OrgID: orgID,
|
||||
Title: title,
|
||||
Description: "",
|
||||
SignedInUser: u,
|
||||
ParentUID: parentUID,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func setupFolderService(t *testing.T, sqlStore db.DB, cfg *setting.Cfg, features featuremgmt.FeatureToggles) folder.Service {
|
||||
tracer := tracing.InitializeTracerForTest()
|
||||
inProcBus := bus.ProvideBus(tracer)
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
|
@ -28,7 +27,7 @@ func TestIntegration_GetUserVisibleNamespaces(t *testing.T) {
|
|||
|
||||
sqlStore := db.InitTestDB(t)
|
||||
cfg := setting.NewCfg()
|
||||
folderService := setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures())
|
||||
folderService := foldertest.NewFakeService()
|
||||
b := &fakeBus{}
|
||||
logger := log.New("test-dbstore")
|
||||
store := createTestStore(sqlStore, folderService, logger, cfg.UnifiedAlerting, b)
|
||||
|
@ -40,18 +39,14 @@ func TestIntegration_GetUserVisibleNamespaces(t *testing.T) {
|
|||
IsGrafanaAdmin: true,
|
||||
}
|
||||
|
||||
folders := []struct {
|
||||
uid string
|
||||
title string
|
||||
parentUid string
|
||||
}{
|
||||
{uid: uuid.NewString(), title: "folder1", parentUid: ""},
|
||||
{uid: uuid.NewString(), title: "folder2", parentUid: ""},
|
||||
{uid: uuid.NewString(), title: "nested/folder", parentUid: ""},
|
||||
folders := []*folder.Folder{
|
||||
{UID: uuid.NewString(), Title: "folder1", ParentUID: "", OrgID: 1},
|
||||
{UID: uuid.NewString(), Title: "folder2", ParentUID: "", OrgID: 1},
|
||||
{UID: uuid.NewString(), Title: "nested/folder", ParentUID: "", OrgID: 1},
|
||||
}
|
||||
|
||||
for _, f := range folders {
|
||||
createFolder(t, store, f.uid, f.title, 1, f.parentUid)
|
||||
folderService.AddFolder(f)
|
||||
}
|
||||
|
||||
t.Run("returns all folders", func(t *testing.T) {
|
||||
|
|
|
@ -24,13 +24,12 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
ngalertfakes "github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/testutil"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
|
@ -83,9 +82,8 @@ func SetupTestEnv(tb testing.TB, baseInterval time.Duration, opts ...TestEnvOpti
|
|||
|
||||
tracer := tracing.InitializeTracerForTest()
|
||||
bus := bus.ProvideBus(tracer)
|
||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
||||
dashboardService, dashboardStore := testutil.SetupDashboardService(tb, sqlStore, folderStore, cfg)
|
||||
folderService := testutil.SetupFolderService(tb, cfg, sqlStore, dashboardStore, folderStore, bus, options.featureToggles, ac)
|
||||
folderService := foldertest.NewFakeService()
|
||||
dashboardService := dashboards.NewFakeDashboardService(tb)
|
||||
ruleStore, err := store.ProvideDBStore(cfg, options.featureToggles, sqlStore, folderService, &dashboards.FakeDashboardService{}, ac, bus)
|
||||
require.NoError(tb, err)
|
||||
ng, err := ngalert.ProvideService(
|
||||
|
|
|
@ -504,8 +504,10 @@ func setupEnv(t *testing.T, sqlStore db.DB, cfg *setting.Cfg, b bus.Bus, quotaSe
|
|||
folderSvc := folderimpl.ProvideService(
|
||||
fStore, acmock.New(), bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore,
|
||||
nil, sqlStore, featuremgmt.WithFeatures(), supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService(), apiserver.WithoutRestConfig)
|
||||
orgService, err := orgimpl.ProvideService(sqlStore, cfg, quotaService)
|
||||
require.NoError(t, err)
|
||||
dashService, err := dashService.ProvideDashboardServiceImpl(cfg, dashStore, folderStore, featuremgmt.WithFeatures(), acmock.NewMockedPermissionsService(),
|
||||
ac, actest.FakeService{}, folderSvc, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(),
|
||||
ac, actest.FakeService{}, folderSvc, nil, client.MockTestRestConfig{}, nil, quotaService, orgService, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(),
|
||||
serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()),
|
||||
kvstore.NewFakeKVStore())
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -10,13 +10,9 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
|
@ -25,14 +21,11 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/search/sort"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
)
|
||||
|
||||
|
@ -826,31 +819,42 @@ func setupNestedTest(t *testing.T, usr *user.SignedInUser, perms []accesscontrol
|
|||
// dashboard store commands that should be called.
|
||||
dashStore, err := database.ProvideDashboardStore(db, cfg, features, tagimpl.ProvideService(db))
|
||||
require.NoError(t, err)
|
||||
|
||||
fStore := folderimpl.ProvideStore(db)
|
||||
folderSvc := folderimpl.ProvideService(
|
||||
fStore, actest.FakeAccessControl{ExpectedEvaluate: true}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderimpl.ProvideDashboardFolderStore(db),
|
||||
nil, db, features, supportbundlestest.NewFakeBundleService(), nil, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService(), apiserver.WithoutRestConfig)
|
||||
|
||||
// create parent folder
|
||||
parent, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
UID: "parent",
|
||||
OrgID: orgID,
|
||||
// create in both the folder & dashboard tables
|
||||
parent, err := fStore.Create(context.Background(), folder.CreateFolderCommand{
|
||||
Title: "parent",
|
||||
SignedInUser: usr,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// create subfolder
|
||||
subfolder, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
||||
UID: "subfolder",
|
||||
ParentUID: "parent",
|
||||
OrgID: orgID,
|
||||
Title: "subfolder",
|
||||
UID: "parent",
|
||||
SignedInUser: usr,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = dashStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{
|
||||
OrgID: orgID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||
"title": "parent",
|
||||
"uid": parent.UID,
|
||||
}),
|
||||
IsFolder: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
subfolder, err := fStore.Create(context.Background(), folder.CreateFolderCommand{
|
||||
Title: "subfolder",
|
||||
OrgID: orgID,
|
||||
UID: "subfolder",
|
||||
ParentUID: parent.UID,
|
||||
SignedInUser: usr,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = dashStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{
|
||||
OrgID: orgID,
|
||||
FolderUID: parent.UID,
|
||||
Dashboard: simplejson.NewFromAny(map[string]any{
|
||||
"title": "subfolder",
|
||||
"uid": subfolder.UID,
|
||||
}),
|
||||
IsFolder: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
// create a root level dashboard
|
||||
_, err = dashStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{
|
||||
OrgID: orgID,
|
||||
|
|
|
@ -158,11 +158,6 @@ func (ss *sqlStatsService) GetSystemStats(ctx context.Context, query *stats.GetS
|
|||
if ss.IsUnifiedAlertingEnabled() {
|
||||
sb.Write(`(SELECT COUNT(DISTINCT (` + dialect.Quote("rule_group") + `)) FROM ` + dialect.Quote("alert_rule") + `) AS rule_groups,`)
|
||||
}
|
||||
// currently not supported when dashboards are in unified storage
|
||||
if !ss.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
||||
sb.Write(`(SELECT SUM(LENGTH(data)) FROM `+dialect.Quote("dashboard")+` WHERE is_folder = ?) AS dashboard_bytes_total,`, dialect.BooleanValue(false))
|
||||
sb.Write(`(SELECT MAX(LENGTH(data)) FROM `+dialect.Quote("dashboard")+` WHERE is_folder = ?) AS dashboard_bytes_max,`, dialect.BooleanValue(false))
|
||||
}
|
||||
|
||||
sb.Write(ss.roleCounterSQL(ctx))
|
||||
|
||||
|
|
|
@ -62,7 +62,6 @@ protocol = https
|
|||
|
||||
[feature_toggles]
|
||||
grafanaAPIServerWithExperimentalAPIs = true
|
||||
kubernetesClientDashboardsFolders = true
|
||||
|
||||
[unified_storage.folders.folder.grafana.app]
|
||||
dualWriterMode = 4
|
||||
|
@ -259,7 +258,6 @@ To enable it, add the following to your `custom.ini` under the `[feature_toggles
|
|||
[feature_toggles]
|
||||
; Used by the Grafana instance
|
||||
unifiedStorageSearchUI = true
|
||||
kubernetesClientDashboardsFolders = true
|
||||
|
||||
; Used by unified storage
|
||||
unifiedStorageSearch = true
|
||||
|
@ -369,7 +367,6 @@ signing_keys_url = http://localhost:3011/api/signing-keys/keys
|
|||
mode = "on-prem"
|
||||
|
||||
[feature_toggles]
|
||||
kubernetesClientDashboardsFolders = true
|
||||
kubernetesDashboardsAPI = true
|
||||
kubernetesFolders = true
|
||||
unifiedStorage = true
|
||||
|
@ -418,7 +415,6 @@ http_port = 3011
|
|||
http_addr = "127.0.0.2"
|
||||
|
||||
[feature_toggles]
|
||||
kubernetesClientDashboardsFolders = true
|
||||
kubernetesDashboardsAPI = true
|
||||
kubernetesFolders = true
|
||||
unifiedStorageSearchUI = true
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
||||
|
@ -42,7 +41,6 @@ func TestIntegrationDashboardServiceValidation(t *testing.T) {
|
|||
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
|
||||
|
@ -279,7 +277,6 @@ func TestIntegrationDashboardQuota(t *testing.T) {
|
|||
DisableAnonymous: true,
|
||||
EnableQuota: true,
|
||||
DashboardOrgQuota: &dashboardQuota,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
|
@ -338,7 +335,6 @@ func TestIntegrationUpdatingProvisionionedDashboards(t *testing.T) {
|
|||
// Setup Grafana and its Database
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
|
||||
provDashboardsDir := filepath.Join(dir, "conf", "provisioning", "dashboards")
|
||||
|
@ -492,7 +488,6 @@ func TestIntegrationCreate(t *testing.T) {
|
|||
// Setup Grafana and its Database
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
|
@ -648,7 +643,6 @@ func intPtr(n int) *int {
|
|||
func TestIntegrationPreserveSchemaVersion(t *testing.T) {
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
|
@ -739,7 +733,6 @@ func TestIntegrationPreserveSchemaVersion(t *testing.T) {
|
|||
func TestIntegrationImportDashboardWithLibraryPanels(t *testing.T) {
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
|
@ -997,7 +990,6 @@ func TestIntegrationDashboardServicePermissions(t *testing.T) {
|
|||
|
||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
tests.CreateUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||
|
|
|
@ -46,7 +46,7 @@ func TestIntegrationFolderServiceGetFolder(t *testing.T) {
|
|||
EnableUnifiedAlerting: true,
|
||||
DisableAnonymous: true,
|
||||
AppModeProduction: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders, featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, p)
|
||||
|
@ -116,7 +116,6 @@ func TestIntegrationUpdateFolder(t *testing.T) {
|
|||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableQuota: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
|
@ -159,7 +158,6 @@ func TestIntegrationCreateFolder(t *testing.T) {
|
|||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableQuota: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
|
@ -208,7 +206,6 @@ func TestIntegrationNestedFoldersOn(t *testing.T) {
|
|||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableQuota: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||
|
@ -362,7 +359,7 @@ func TestIntegrationSharedWithMe(t *testing.T) {
|
|||
EnableUnifiedAlerting: true,
|
||||
DisableAnonymous: true,
|
||||
AppModeProduction: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders, featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
||||
|
@ -416,7 +413,6 @@ func TestIntegrationBasicRoles(t *testing.T) {
|
|||
EnableUnifiedAlerting: true,
|
||||
DisableAnonymous: true,
|
||||
AppModeProduction: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
||||
|
@ -554,7 +550,7 @@ func TestIntegrationFineGrainedPermissions(t *testing.T) {
|
|||
EnableUnifiedAlerting: true,
|
||||
DisableAnonymous: true,
|
||||
AppModeProduction: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders, featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
||||
|
|
|
@ -35,7 +35,6 @@ func TestGetFolders(t *testing.T) {
|
|||
EnableUnifiedAlerting: true,
|
||||
DisableAnonymous: true,
|
||||
AppModeProduction: true,
|
||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
||||
})
|
||||
|
||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
||||
|
|
|
@ -222,12 +222,7 @@ func TestIntegrationLegacySupport(t *testing.T) {
|
|||
t.Skip("skipping integration test in short mode")
|
||||
}
|
||||
ctx := context.Background()
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
EnableFeatureToggles: []string{
|
||||
// NOTE: when using this feature toggle, the read is always v0!
|
||||
// featuremgmt.FlagKubernetesClientDashboardsFolders
|
||||
},
|
||||
})
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{})
|
||||
|
||||
clientV0 := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: helper.Org1.Admin,
|
||||
|
|
|
@ -69,7 +69,6 @@ func TestIntegrationDashboardAPIValidation(t *testing.T) {
|
|||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Enable dashboard feature
|
||||
featuremgmt.FlagUnifiedStorageSearch,
|
||||
featuremgmt.FlagKubernetesDashboards, // Enable FE-only dashboard feature flag
|
||||
},
|
||||
|
@ -101,7 +100,6 @@ func TestIntegrationDashboardAPIValidation(t *testing.T) {
|
|||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Enable dashboard feature
|
||||
featuremgmt.FlagUnifiedStorageSearch,
|
||||
},
|
||||
DisableFeatureToggles: []string{
|
||||
|
@ -138,7 +136,6 @@ func TestIntegrationDashboardAPIAuthorization(t *testing.T) {
|
|||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Enable dashboard feature
|
||||
featuremgmt.FlagUnifiedStorageSearch,
|
||||
},
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
|
@ -189,7 +186,6 @@ func TestIntegrationDashboardAPI(t *testing.T) {
|
|||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Enable dashboard feature
|
||||
featuremgmt.FlagUnifiedStorageSearch,
|
||||
featuremgmt.FlagKubernetesDashboards,
|
||||
},
|
||||
|
|
|
@ -32,7 +32,6 @@ func TestIntegrationLibraryPanelConnections(t *testing.T) {
|
|||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
"unifiedStorageSearch",
|
||||
"kubernetesClientDashboardsFolders",
|
||||
"kubernetesLibraryPanels",
|
||||
},
|
||||
})
|
||||
|
@ -98,7 +97,6 @@ func TestIntegrationLibraryElementPermissions(t *testing.T) {
|
|||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
"unifiedStorageSearch",
|
||||
"kubernetesClientDashboardsFolders",
|
||||
"kubernetesLibraryPanels",
|
||||
"grafanaAPIServerWithExperimentalAPIs", // needed until we move it to v0beta1 at least (currently v0alpha1)
|
||||
},
|
||||
|
@ -303,7 +301,6 @@ func TestIntegrationLibraryPanelConnectionsWithFolderAccess(t *testing.T) {
|
|||
EnableFeatureToggles: []string{
|
||||
"unifiedStorageSearch",
|
||||
"kubernetesLibraryPanels",
|
||||
"kubernetesClientDashboardsFolders",
|
||||
},
|
||||
})
|
||||
ctx := createTestContext(t, helper, helper.Org1, dualWriterMode)
|
||||
|
|
|
@ -51,9 +51,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
|||
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
},
|
||||
EnableFeatureToggles: []string{},
|
||||
})
|
||||
|
||||
t.Run("Check discovery client", func(t *testing.T) {
|
||||
|
@ -138,9 +136,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
|||
DualWriterMode: modeDw,
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
},
|
||||
EnableFeatureToggles: []string{},
|
||||
}))
|
||||
})
|
||||
|
||||
|
@ -155,7 +151,6 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
|||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
|
@ -172,7 +167,6 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
|||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
|
@ -189,7 +183,6 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
|||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
|
@ -206,7 +199,6 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
|||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
|
@ -237,7 +229,6 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
|||
// We set it to 1 here, so we always get forced pagination based on the response size.
|
||||
UnifiedStorageMaxPageSizeBytes: 1,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}), mode)
|
||||
|
@ -680,7 +671,6 @@ func TestIntegrationFolderCreatePermissions(t *testing.T) {
|
|||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagNestedFolders,
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -788,7 +778,6 @@ func TestIntegrationFolderGetPermissions(t *testing.T) {
|
|||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagNestedFolders,
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -972,7 +961,6 @@ func TestIntegrationFoldersCreateAPIEndpointK8S(t *testing.T) {
|
|||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagNestedFolders,
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -1148,7 +1136,6 @@ func TestIntegrationFoldersGetAPIEndpointK8S(t *testing.T) {
|
|||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagNestedFolders,
|
||||
featuremgmt.FlagUnifiedStorageSearch,
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ func TestIntegrationOpenAPIs(t *testing.T) {
|
|||
h := NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Will be default on by G12
|
||||
featuremgmt.FlagQueryService, // Query Library
|
||||
featuremgmt.FlagProvisioning,
|
||||
featuremgmt.FlagInvestigationsBackend,
|
||||
|
|
|
@ -221,7 +221,6 @@ func runGrafana(t *testing.T, options ...grafanaOption) *provisioningTestHelper
|
|||
AppModeProduction: false, // required for experimental APIs
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagProvisioning,
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
},
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"dashboards.dashboard.grafana.app": {
|
||||
|
|
|
@ -383,13 +383,8 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> impleme
|
|||
}
|
||||
|
||||
public onRestore = async (version: DecoratedRevisionModel): Promise<boolean> => {
|
||||
let versionRsp;
|
||||
if (config.featureToggles.kubernetesClientDashboardsFolders) {
|
||||
// the id here is the resource version in k8s, use this instead to get the specific version
|
||||
versionRsp = await historySrv.restoreDashboard(version.uid, version.id);
|
||||
} else {
|
||||
versionRsp = await historySrv.restoreDashboard(version.uid, version.version);
|
||||
}
|
||||
let versionRsp = await historySrv.restoreDashboard(version.uid, version.id);
|
||||
|
||||
if (!Number.isInteger(versionRsp.version)) {
|
||||
return false;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { config } from '@grafana/runtime';
|
||||
import { SceneTimeRange } from '@grafana/scenes';
|
||||
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
|
@ -149,10 +148,7 @@ describe('VersionsEditView', () => {
|
|||
expect(versionsView.versions.find((rev) => rev.version === 1)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should correctly identify last page when kubernetesClientDashboardsFolders is enabled and continueToken is empty', async () => {
|
||||
// @ts-ignore
|
||||
config.featureToggles.kubernetesClientDashboardsFolders = true;
|
||||
|
||||
it('should correctly identify last page when continueToken is empty', async () => {
|
||||
jest.mocked(historySrv.getHistoryList).mockResolvedValueOnce({
|
||||
continueToken: '',
|
||||
versions: [
|
||||
|
@ -190,10 +186,6 @@ describe('VersionsEditView', () => {
|
|||
expect(versionsView.versions.length).toBeLessThan(VERSIONS_FETCH_LIMIT);
|
||||
expect(versionsView.versions.find((rev) => rev.version === 1)).toBeUndefined();
|
||||
expect(versionsView.continueToken).toBe('');
|
||||
|
||||
// reset feature flag
|
||||
// @ts-ignore
|
||||
config.featureToggles.kubernetesClientDashboardsFolders = false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { PageLayoutType, dateTimeFormat, dateTimeFormatTimeAgo } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { SceneComponentProps, SceneObjectBase, sceneGraph } from '@grafana/scenes';
|
||||
import { Spinner, Stack } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
|
@ -136,15 +135,9 @@ export class VersionsEditView extends SceneObjectBase<VersionsEditViewState> imp
|
|||
if (!this._dashboard.state.uid) {
|
||||
return;
|
||||
}
|
||||
let lhs, rhs;
|
||||
if (config.featureToggles.kubernetesClientDashboardsFolders) {
|
||||
// the id here is the resource version in k8s, use this instead to get the specific version
|
||||
lhs = await historySrv.getDashboardVersion(this._dashboard.state.uid, baseInfo.id);
|
||||
rhs = await historySrv.getDashboardVersion(this._dashboard.state.uid, newInfo.id);
|
||||
} else {
|
||||
lhs = await historySrv.getDashboardVersion(this._dashboard.state.uid, baseInfo.version);
|
||||
rhs = await historySrv.getDashboardVersion(this._dashboard.state.uid, newInfo.version);
|
||||
}
|
||||
let lhs = await historySrv.getDashboardVersion(this._dashboard.state.uid, baseInfo.id);
|
||||
let rhs = await historySrv.getDashboardVersion(this._dashboard.state.uid, newInfo.id);
|
||||
|
||||
this.setState({
|
||||
baseInfo,
|
||||
|
@ -204,10 +197,10 @@ function VersionsEditorSettingsListView({ model }: SceneComponentProps<VersionsE
|
|||
const showButtons = model.versions.length > 1;
|
||||
const hasMore = model.versions.length >= model.limit;
|
||||
// older versions may have been cleaned up in the db, so also check if the last page is less than the limit, if so, we are at the end
|
||||
let isLastPage = model.versions.find((rev) => rev.version === 1) || model.versions.length % model.limit !== 0;
|
||||
if (config.featureToggles.kubernetesClientDashboardsFolders) {
|
||||
isLastPage = isLastPage || model.continueToken === '';
|
||||
}
|
||||
let isLastPage =
|
||||
model.versions.find((rev) => rev.version === 1) ||
|
||||
model.versions.length % model.limit !== 0 ||
|
||||
model.continueToken === '';
|
||||
|
||||
const viewModeCompare = (
|
||||
<>
|
||||
|
|
|
@ -2,7 +2,6 @@ import { screen, waitFor, within } from '@testing-library/react';
|
|||
import userEvent from '@testing-library/user-event';
|
||||
import { render } from 'test/test-utils';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
import { historySrv } from 'app/features/dashboard-scene/settings/version-history/HistorySrv';
|
||||
|
||||
import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures';
|
||||
|
@ -110,7 +109,7 @@ describe('VersionSettings', () => {
|
|||
|
||||
test('renders buttons if versions >= VERSIONS_FETCH_LIMIT', async () => {
|
||||
historySrv.getHistoryList = jest.fn().mockResolvedValue({
|
||||
continueToken: versions.continueToken,
|
||||
continueToken: 'next-page-token',
|
||||
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT),
|
||||
});
|
||||
|
||||
|
@ -135,13 +134,13 @@ describe('VersionSettings', () => {
|
|||
.fn()
|
||||
.mockImplementationOnce(() =>
|
||||
Promise.resolve({
|
||||
continueToken: versions.continueToken,
|
||||
continueToken: 'next-page-token',
|
||||
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT),
|
||||
})
|
||||
)
|
||||
.mockImplementationOnce(() =>
|
||||
Promise.resolve({
|
||||
continueToken: versions.continueToken,
|
||||
continueToken: '',
|
||||
versions: versions.versions.slice(VERSIONS_FETCH_LIMIT),
|
||||
})
|
||||
);
|
||||
|
@ -184,8 +183,7 @@ describe('VersionSettings', () => {
|
|||
expect(screen.getByRole('button', { name: /compare versions/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('does not show more button when kubernetesClientDashboardsFolders is enabled and continueToken is empty', async () => {
|
||||
config.featureToggles.kubernetesClientDashboardsFolders = true;
|
||||
test('does not show more button when continueToken is empty', async () => {
|
||||
historySrv.getHistoryList = jest.fn().mockResolvedValueOnce({
|
||||
continueToken: '',
|
||||
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT - 1),
|
||||
|
@ -197,8 +195,6 @@ describe('VersionSettings', () => {
|
|||
|
||||
expect(screen.queryByRole('button', { name: /show more versions/i })).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: /compare versions/i })).toBeInTheDocument();
|
||||
|
||||
config.featureToggles.kubernetesClientDashboardsFolders = false;
|
||||
});
|
||||
|
||||
test('selecting two versions and clicking compare button should render compare view', async () => {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { PureComponent } from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Spinner, HorizontalGroup } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { historySrv, RevisionsModel } from 'app/features/dashboard-scene/settings/version-history/HistorySrv';
|
||||
|
@ -90,15 +89,9 @@ export class VersionsSettings extends PureComponent<Props, State> {
|
|||
isLoading: true,
|
||||
});
|
||||
|
||||
let lhs, rhs;
|
||||
if (config.featureToggles.kubernetesClientDashboardsFolders) {
|
||||
// the id here is the resource version in k8s, use this instead to get the specific version
|
||||
lhs = await historySrv.getDashboardVersion(this.props.dashboard.uid, baseInfo.id);
|
||||
rhs = await historySrv.getDashboardVersion(this.props.dashboard.uid, newInfo.id);
|
||||
} else {
|
||||
lhs = await historySrv.getDashboardVersion(this.props.dashboard.uid, baseInfo.version);
|
||||
rhs = await historySrv.getDashboardVersion(this.props.dashboard.uid, newInfo.version);
|
||||
}
|
||||
let lhs = await historySrv.getDashboardVersion(this.props.dashboard.uid, baseInfo.id);
|
||||
let rhs = await historySrv.getDashboardVersion(this.props.dashboard.uid, newInfo.id);
|
||||
|
||||
this.setState({
|
||||
baseInfo,
|
||||
|
@ -122,15 +115,12 @@ export class VersionsSettings extends PureComponent<Props, State> {
|
|||
}));
|
||||
|
||||
isLastPage() {
|
||||
if (config.featureToggles.kubernetesClientDashboardsFolders) {
|
||||
return (
|
||||
this.state.versions.find((rev) => rev.version === 1) ||
|
||||
this.state.versions.length % this.limit !== 0 ||
|
||||
this.continueToken === ''
|
||||
);
|
||||
}
|
||||
return this.state.versions.find((rev) => rev.version === 1) || this.state.versions.length % this.limit !== 0;
|
||||
}
|
||||
|
||||
onCheck = (ev: React.FormEvent<HTMLInputElement>, versionId: number) => {
|
||||
this.setState({
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useEffect } from 'react';
|
|||
import { useAsyncFn } from 'react-use';
|
||||
|
||||
import { locationUtil } from '@grafana/data';
|
||||
import { config, locationService } from '@grafana/runtime';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { useAppNotification } from 'app/core/copy/appNotification';
|
||||
import { historySrv } from 'app/features/dashboard-scene/settings/version-history/HistorySrv';
|
||||
import { useSelector } from 'app/types/store';
|
||||
|
@ -18,11 +18,7 @@ const restoreDashboard = async (version: number, dashboard: DashboardModel) => {
|
|||
|
||||
export const useDashboardRestore = (id: number, version: number) => {
|
||||
const dashboard = useSelector((state) => state.dashboard.getModel());
|
||||
const [state, onRestoreDashboard] = useAsyncFn(
|
||||
async () =>
|
||||
await restoreDashboard(config.featureToggles.kubernetesClientDashboardsFolders ? id : version, dashboard!),
|
||||
[]
|
||||
);
|
||||
const [state, onRestoreDashboard] = useAsyncFn(async () => await restoreDashboard(id, dashboard!), []);
|
||||
const notifyApp = useAppNotification();
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import { FeatureToggles } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
export const requiredFeatureToggles: Array<keyof FeatureToggles> = [
|
||||
'provisioning',
|
||||
'kubernetesDashboards',
|
||||
'kubernetesClientDashboardsFolders',
|
||||
];
|
||||
export const requiredFeatureToggles: Array<keyof FeatureToggles> = ['provisioning', 'kubernetesDashboards'];
|
||||
|
||||
/**
|
||||
* Checks if all required feature toggles are enabled
|
||||
|
|
Loading…
Reference in New Issue