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 |
|
| `dashgpt` | Enable AI powered features in dashboards | Yes |
|
||||||
| `panelMonitoring` | Enables panel monitoring through logs and measurements | Yes |
|
| `panelMonitoring` | Enables panel monitoring through logs and measurements | Yes |
|
||||||
| `formatString` | Enable format string transformer | 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 |
|
| `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 |
|
| `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 |
|
| `dashboardSceneForViewers` | Enables dashboard rendering using Scenes for viewer roles | Yes |
|
||||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
||||||
unifiedStorageSearchSprinkles = true
|
unifiedStorageSearchSprinkles = true
|
||||||
kubernetesFoldersServiceV2 = true
|
kubernetesFoldersServiceV2 = true
|
||||||
unifiedStorageSearchPermissionFiltering = true
|
unifiedStorageSearchPermissionFiltering = true
|
||||||
kubernetesClientDashboardsFolders = true
|
|
||||||
|
|
||||||
[unified_storage.folders.folder.grafana.app]
|
[unified_storage.folders.folder.grafana.app]
|
||||||
dualWriterMode = 0
|
dualWriterMode = 0
|
||||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
||||||
unifiedStorageSearchSprinkles = true
|
unifiedStorageSearchSprinkles = true
|
||||||
kubernetesFoldersServiceV2 = true
|
kubernetesFoldersServiceV2 = true
|
||||||
unifiedStorageSearchPermissionFiltering = true
|
unifiedStorageSearchPermissionFiltering = true
|
||||||
kubernetesClientDashboardsFolders = true
|
|
||||||
|
|
||||||
[unified_storage.folders.folder.grafana.app]
|
[unified_storage.folders.folder.grafana.app]
|
||||||
dualWriterMode = 1
|
dualWriterMode = 1
|
||||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
||||||
unifiedStorageSearchSprinkles = true
|
unifiedStorageSearchSprinkles = true
|
||||||
kubernetesFoldersServiceV2 = true
|
kubernetesFoldersServiceV2 = true
|
||||||
unifiedStorageSearchPermissionFiltering = true
|
unifiedStorageSearchPermissionFiltering = true
|
||||||
kubernetesClientDashboardsFolders = true
|
|
||||||
|
|
||||||
[unified_storage.folders.folder.grafana.app]
|
[unified_storage.folders.folder.grafana.app]
|
||||||
dualWriterMode = 2
|
dualWriterMode = 2
|
||||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
||||||
unifiedStorageSearchSprinkles = true
|
unifiedStorageSearchSprinkles = true
|
||||||
kubernetesFoldersServiceV2 = true
|
kubernetesFoldersServiceV2 = true
|
||||||
unifiedStorageSearchPermissionFiltering = true
|
unifiedStorageSearchPermissionFiltering = true
|
||||||
kubernetesClientDashboardsFolders = true
|
|
||||||
|
|
||||||
[unified_storage.folders.folder.grafana.app]
|
[unified_storage.folders.folder.grafana.app]
|
||||||
dualWriterMode = 2
|
dualWriterMode = 2
|
||||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
||||||
unifiedStorageSearchSprinkles = true
|
unifiedStorageSearchSprinkles = true
|
||||||
kubernetesFoldersServiceV2 = true
|
kubernetesFoldersServiceV2 = true
|
||||||
unifiedStorageSearchPermissionFiltering = true
|
unifiedStorageSearchPermissionFiltering = true
|
||||||
kubernetesClientDashboardsFolders = true
|
|
||||||
|
|
||||||
[unified_storage.folders.folder.grafana.app]
|
[unified_storage.folders.folder.grafana.app]
|
||||||
dualWriterMode = 3
|
dualWriterMode = 3
|
||||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
||||||
unifiedStorageSearchSprinkles = true
|
unifiedStorageSearchSprinkles = true
|
||||||
kubernetesFoldersServiceV2 = true
|
kubernetesFoldersServiceV2 = true
|
||||||
unifiedStorageSearchPermissionFiltering = true
|
unifiedStorageSearchPermissionFiltering = true
|
||||||
kubernetesClientDashboardsFolders = true
|
|
||||||
|
|
||||||
[unified_storage.folders.folder.grafana.app]
|
[unified_storage.folders.folder.grafana.app]
|
||||||
dualWriterMode = 4
|
dualWriterMode = 4
|
||||||
|
|
|
@ -10,7 +10,6 @@ kubernetesCliDashboards = true
|
||||||
unifiedStorageSearchSprinkles = true
|
unifiedStorageSearchSprinkles = true
|
||||||
kubernetesFoldersServiceV2 = true
|
kubernetesFoldersServiceV2 = true
|
||||||
unifiedStorageSearchPermissionFiltering = true
|
unifiedStorageSearchPermissionFiltering = true
|
||||||
kubernetesClientDashboardsFolders = true
|
|
||||||
|
|
||||||
[unified_storage.folders.folder.grafana.app]
|
[unified_storage.folders.folder.grafana.app]
|
||||||
dualWriterMode = 5
|
dualWriterMode = 5
|
||||||
|
|
|
@ -290,11 +290,6 @@ export interface FeatureToggles {
|
||||||
*/
|
*/
|
||||||
kubernetesDashboards?: boolean;
|
kubernetesDashboards?: boolean;
|
||||||
/**
|
/**
|
||||||
* Route the folder and dashboard service requests to k8s
|
|
||||||
* @default true
|
|
||||||
*/
|
|
||||||
kubernetesClientDashboardsFolders?: boolean;
|
|
||||||
/**
|
|
||||||
* Disable schema validation for dashboards/v1
|
* Disable schema validation for dashboards/v1
|
||||||
*/
|
*/
|
||||||
dashboardDisableSchemaValidationV1?: boolean;
|
dashboardDisableSchemaValidationV1?: boolean;
|
||||||
|
|
|
@ -204,7 +204,7 @@ func (hs *HTTPServer) GetDashboard(c *contextmodel.ReqContext) response.Response
|
||||||
}
|
}
|
||||||
metrics.MFolderIDsAPICount.WithLabelValues(metrics.GetDashboard).Inc()
|
metrics.MFolderIDsAPICount.WithLabelValues(metrics.GetDashboard).Inc()
|
||||||
// lookup folder title & url
|
// lookup folder title & url
|
||||||
if dash.FolderUID != "" && hs.Features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
if dash.FolderUID != "" {
|
||||||
queryResult, err := hs.folderService.Get(ctx, &folder.GetFolderQuery{
|
queryResult, err := hs.folderService.Get(ctx, &folder.GetFolderQuery{
|
||||||
OrgID: c.GetOrgID(),
|
OrgID: c.GetOrgID(),
|
||||||
UID: &dash.FolderUID,
|
UID: &dash.FolderUID,
|
||||||
|
|
|
@ -19,32 +19,23 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
"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/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
"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/localcache"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"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/tracing"
|
||||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
"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/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"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"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"
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest"
|
"github.com/grafana/grafana/pkg/services/dashboardversion/dashvertest"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"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/folder/foldertest"
|
||||||
libraryelementsfake "github.com/grafana/grafana/pkg/services/libraryelements/fake"
|
libraryelementsfake "github.com/grafana/grafana/pkg/services/libraryelements/fake"
|
||||||
"github.com/grafana/grafana/pkg/services/librarypanels"
|
"github.com/grafana/grafana/pkg/services/librarypanels"
|
||||||
|
@ -59,14 +50,10 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/services/publicdashboards/api"
|
"github.com/grafana/grafana/pkg/services/publicdashboards/api"
|
||||||
publicdashboardModels "github.com/grafana/grafana/pkg/services/publicdashboards/models"
|
publicdashboardModels "github.com/grafana/grafana/pkg/services/publicdashboards/models"
|
||||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
"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/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"
|
||||||
"github.com/grafana/grafana/pkg/services/user/usertest"
|
"github.com/grafana/grafana/pkg/services/user/usertest"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/web/webtest"
|
"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) {
|
t.Run("Given provisioned dashboard", func(t *testing.T) {
|
||||||
mockSQLStore := dbtest.NewFakeDB()
|
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)
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||||
|
dashboardProvisioningService := dashboards.NewFakeDashboardProvisioning(t)
|
||||||
|
|
||||||
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "editable": true, "style": "dark"}`))
|
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "editable": true, "style": "dark"}`))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
qResult := &dashboards.Dashboard{ID: 1, Data: dataValue}
|
qResult := &dashboards.Dashboard{ID: 1, Data: dataValue}
|
||||||
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil)
|
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) {
|
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 := provisioning.NewProvisioningServiceMock(context.Background())
|
||||||
fakeProvisioningService.GetDashboardProvisionerResolvedPathFunc = func(name string) string {
|
fakeProvisioningService.GetDashboardProvisionerResolvedPathFunc = func(name string) string {
|
||||||
return "/tmp/grafana/dashboards"
|
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)
|
}, 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) {
|
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,
|
ProvisioningService: fakeProvisioningService,
|
||||||
LibraryPanelService: &mockLibraryPanelService{},
|
LibraryPanelService: &mockLibraryPanelService{},
|
||||||
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
LibraryElementService: &libraryelementsfake.LibraryElementService{},
|
||||||
dashboardProvisioningService: mockDashboardProvisioningService{},
|
dashboardProvisioningService: dashboardProvisioningService,
|
||||||
SQLStore: mockSQLStore,
|
SQLStore: mockSQLStore,
|
||||||
AccessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
AccessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||||
DashboardService: dashboardService,
|
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) {
|
t.Run("v2 dashboards should not be returned in api", func(t *testing.T) {
|
||||||
mockSQLStore := dbtest.NewFakeDB()
|
mockSQLStore := dbtest.NewFakeDB()
|
||||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||||
|
dashboardProvisioningService := dashboards.NewFakeDashboardProvisioning(t)
|
||||||
|
|
||||||
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "apiVersion": "v2"}`))
|
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "apiVersion": "v2"}`))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -794,7 +799,7 @@ func TestIntegrationDashboardAPIEndpoint(t *testing.T) {
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
starService: startest.NewStarServiceFake(),
|
starService: startest.NewStarServiceFake(),
|
||||||
tracer: tracing.InitializeTracerForTest(),
|
tracer: tracing.InitializeTracerForTest(),
|
||||||
dashboardProvisioningService: mockDashboardProvisioningService{},
|
dashboardProvisioningService: dashboardProvisioningService,
|
||||||
folderService: foldertest.NewFakeService(),
|
folderService: foldertest.NewFakeService(),
|
||||||
log: log.New("test"),
|
log: log.New("test"),
|
||||||
namespacer: func(orgID int64) string { return strconv.FormatInt(orgID, 10) },
|
namespacer: func(orgID int64) string { return strconv.FormatInt(orgID, 10) },
|
||||||
|
@ -912,79 +917,6 @@ func TestDashboardVersionsAPIEndpoint(t *testing.T) {
|
||||||
}, mockSQLStore)
|
}, 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) {
|
func (hs *HTTPServer) callGetDashboard(sc *scenarioContext) {
|
||||||
sc.handlerFunc = hs.GetDashboard
|
sc.handlerFunc = hs.GetDashboard
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
claims "github.com/grafana/authlib/types"
|
|
||||||
"github.com/grafana/grafana/pkg/api/apierrors"
|
"github.com/grafana/grafana/pkg/api/apierrors"
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"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/infra/metrics"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
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/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
"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/services/search"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
|
@ -198,13 +194,6 @@ func (hs *HTTPServer) CreateFolder(c *contextmodel.ReqContext) response.Response
|
||||||
return apierrors.ToFolderErrorResponse(err)
|
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)
|
folderDTO, err := hs.newToFolderDto(c, folder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Err(err)
|
return response.Err(err)
|
||||||
|
@ -214,46 +203,6 @@ func (hs *HTTPServer) CreateFolder(c *contextmodel.ReqContext) response.Response
|
||||||
return response.JSON(http.StatusOK, folderDTO)
|
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
|
// swagger:route POST /folders/{folder_uid}/move folders moveFolder
|
||||||
//
|
//
|
||||||
// Move folder.
|
// Move folder.
|
||||||
|
|
|
@ -620,9 +620,6 @@ func TestGetFolderLegacyAndUnifiedStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
featuresArr := []any{featuremgmt.FlagNestedFolders}
|
featuresArr := []any{featuremgmt.FlagNestedFolders}
|
||||||
if tc.unifiedStorageEnabled {
|
|
||||||
featuresArr = append(featuresArr, featuremgmt.FlagKubernetesClientDashboardsFolders)
|
|
||||||
}
|
|
||||||
|
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = cfg
|
hs.Cfg = cfg
|
||||||
|
@ -676,39 +673,6 @@ func TestGetFolderLegacyAndUnifiedStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetDefaultPermissionsWhenCreatingFolder(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
|
// we need to save these values because they are defined at `setting` package level
|
||||||
// and modified when we invoke setting.NewCfgFromINIFile
|
// and modified when we invoke setting.NewCfgFromINIFile
|
||||||
prevCookieSameSiteDisabled := setting.CookieSameSiteDisabled
|
prevCookieSameSiteDisabled := setting.CookieSameSiteDisabled
|
||||||
|
@ -725,42 +689,32 @@ func TestSetDefaultPermissionsWhenCreatingFolder(t *testing.T) {
|
||||||
setting.CookieSameSiteDisabled = prevCookieSameSiteDisabled
|
setting.CookieSameSiteDisabled = prevCookieSameSiteDisabled
|
||||||
setting.CookieSameSiteMode = prevCookieSameSiteMode
|
setting.CookieSameSiteMode = prevCookieSameSiteMode
|
||||||
|
|
||||||
for _, tc := range tcs {
|
folderService := &foldertest.FakeService{
|
||||||
t.Run(tc.description, func(t *testing.T) {
|
ExpectedFolder: &folder.Folder{UID: "uid", Title: "Folder"},
|
||||||
folderService.ExpectedFolder = tc.expectedFolder
|
}
|
||||||
folderPermService := acmock.NewMockedPermissionsService()
|
folderPermService := acmock.NewMockedPermissionsService()
|
||||||
folderPermService.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
|
folderPermService.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
|
||||||
|
|
||||||
srv := SetupAPITestServer(t, func(hs *HTTPServer) {
|
srv := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = cfg
|
hs.Cfg = cfg
|
||||||
|
hs.Features = featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
|
||||||
featuresArr := append(tc.featuresArr, featuremgmt.FlagNestedFolders)
|
|
||||||
hs.Features = featuremgmt.WithFeatures(
|
|
||||||
featuresArr...,
|
|
||||||
)
|
|
||||||
hs.folderService = folderService
|
hs.folderService = folderService
|
||||||
hs.folderPermissionsService = folderPermService
|
hs.folderPermissionsService = folderPermService
|
||||||
hs.accesscontrolService = actest.FakeService{}
|
hs.accesscontrolService = actest.FakeService{}
|
||||||
})
|
})
|
||||||
|
|
||||||
input := strings.NewReader(tc.input)
|
input := strings.NewReader("{ \"uid\": \"uid\", \"title\": \"Folder\"}")
|
||||||
req := srv.NewPostRequest("/api/folders", input)
|
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)
|
resp, err := srv.SendJSON(req)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, tc.expectedCode, resp.StatusCode)
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
|
||||||
folder := dtos.Folder{}
|
folder := dtos.Folder{}
|
||||||
err = json.NewDecoder(resp.Body).Decode(&folder)
|
err = json.NewDecoder(resp.Body).Decode(&folder)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, resp.Body.Close())
|
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, "uid", folder.UID)
|
||||||
assert.Equal(t, "Folder", folder.Title)
|
assert.Equal(t, "Folder", folder.Title)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -872,11 +872,9 @@ func setUpServiceTest(t *testing.T, withDashboardMock bool, cfgOverrides ...conf
|
||||||
rr := routing.NewRouteRegister()
|
rr := routing.NewRouteRegister()
|
||||||
tracer := tracing.InitializeTracerForTest()
|
tracer := tracing.InitializeTracerForTest()
|
||||||
|
|
||||||
fakeFolder := &folder.Folder{UID: "folderUID", Title: "Folder"}
|
fakeFolder := &folder.Folder{UID: "folderUID", Title: "Folder", Fullpath: "Folder"}
|
||||||
mockFolder := &foldertest.FakeService{
|
mockFolder := foldertest.NewFakeService()
|
||||||
ExpectedFolders: []*folder.Folder{fakeFolder},
|
mockFolder.AddFolder(fakeFolder)
|
||||||
ExpectedFolder: fakeFolder,
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
section, err := cfg.Raw.NewSection("cloud_migration")
|
section, err := cfg.Raw.NewSection("cloud_migration")
|
||||||
|
|
|
@ -3,6 +3,7 @@ package cloudmigrationimpl
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -18,6 +19,7 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
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/api/tooling/definitions"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"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 {
|
func createAlertRule(t *testing.T, ctx context.Context, service *Service, user *user.SignedInUser, isPaused bool, ruleGroup string) models.AlertRule {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
// Ensure the folder exists before creating alert rules
|
||||||
|
createFolder(t, ctx, service, user, "folderUID", "Test Folder")
|
||||||
|
|
||||||
rule := models.AlertRule{
|
rule := models.AlertRule{
|
||||||
OrgID: user.GetOrgID(),
|
OrgID: user.GetOrgID(),
|
||||||
Title: fmt.Sprintf("Alert Rule SLO (Paused: %v) - %v", isPaused, ruleGroup),
|
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
|
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 {
|
func createAlertRuleGroup(t *testing.T, ctx context.Context, service *Service, user *user.SignedInUser, title string, rules []models.AlertRule) models.AlertRuleGroup {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
|
|
@ -146,14 +146,6 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
|
||||||
}
|
}
|
||||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
|
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)
|
revision := savedDashboard.Data.Get("revision").MustInt64(0)
|
||||||
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
|
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.DashboardImport).Inc()
|
||||||
return &dashboardimport.ImportDashboardResponse{
|
return &dashboardimport.ImportDashboardResponse{
|
||||||
|
|
|
@ -46,16 +46,11 @@ func TestImportDashboardService(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
importLibraryPanelsForDashboard := false
|
importLibraryPanelsForDashboard := false
|
||||||
connectLibraryPanelsForDashboardCalled := false
|
|
||||||
libraryPanelService := &libraryPanelServiceMock{
|
libraryPanelService := &libraryPanelServiceMock{
|
||||||
importLibraryPanelsForDashboardFunc: func(ctx context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error {
|
importLibraryPanelsForDashboardFunc: func(ctx context.Context, signedInUser identity.Requester, libraryPanels *simplejson.Json, panels []any, folderID int64, folderUID string) error {
|
||||||
importLibraryPanelsForDashboard = true
|
importLibraryPanelsForDashboard = true
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
connectLibraryPanelsForDashboardFunc: func(ctx context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error {
|
|
||||||
connectLibraryPanelsForDashboardCalled = true
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
folderService := &foldertest.FakeService{
|
folderService := &foldertest.FakeService{
|
||||||
ExpectedFolder: &folder.Folder{
|
ExpectedFolder: &folder.Folder{
|
||||||
|
@ -98,7 +93,6 @@ func TestImportDashboardService(t *testing.T) {
|
||||||
require.Equal(t, "prom", panel.Get("datasource").MustString())
|
require.Equal(t, "prom", panel.Get("datasource").MustString())
|
||||||
|
|
||||||
require.True(t, importLibraryPanelsForDashboard)
|
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) {
|
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/apimachinery/utils"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"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/services/search/model"
|
||||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||||
|
|
||||||
|
@ -77,12 +76,8 @@ type DashboardProvisioningService interface {
|
||||||
type Store interface {
|
type Store interface {
|
||||||
DeleteDashboard(ctx context.Context, cmd *DeleteDashboardCommand) error
|
DeleteDashboard(ctx context.Context, cmd *DeleteDashboardCommand) error
|
||||||
CleanupAfterDelete(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)
|
FindDashboards(ctx context.Context, query *FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error)
|
||||||
GetDashboard(ctx context.Context, query *GetDashboardQuery) (*Dashboard, 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 retrieves dashboards identified by plugin.
|
||||||
GetDashboardsByPluginID(ctx context.Context, query *GetDashboardsByPluginIDQuery) ([]*Dashboard, error)
|
GetDashboardsByPluginID(ctx context.Context, query *GetDashboardsByPluginIDQuery) ([]*Dashboard, error)
|
||||||
GetDashboardTags(ctx context.Context, query *GetDashboardTagsQuery) ([]*DashboardTagCloudItem, error)
|
GetDashboardTags(ctx context.Context, query *GetDashboardTagsQuery) ([]*DashboardTagCloudItem, error)
|
||||||
|
@ -97,11 +92,7 @@ type Store interface {
|
||||||
// ValidateDashboardBeforeSave validates a dashboard before save.
|
// ValidateDashboardBeforeSave validates a dashboard before save.
|
||||||
ValidateDashboardBeforeSave(ctx context.Context, dashboard *Dashboard, overwrite bool) (bool, error)
|
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)
|
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
|
DeleteDashboardsInFolders(ctx context.Context, request *DeleteDashboardsInFolderRequest) error
|
||||||
|
|
||||||
GetAllDashboardsByOrgId(ctx context.Context, orgID int64) ([]*Dashboard, error)
|
GetAllDashboardsByOrgId(ctx context.Context, orgID int64) ([]*Dashboard, error)
|
||||||
|
|
|
@ -2,10 +2,8 @@ package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
|
@ -25,7 +23,6 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrations"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrations"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
"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/store"
|
||||||
"github.com/grafana/grafana/pkg/services/tag"
|
"github.com/grafana/grafana/pkg/services/tag"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"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) {
|
func (d *dashboardStore) Count(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
|
||||||
ctx, span := tracer.Start(ctx, "dashboards.database.Count")
|
ctx, span := tracer.Start(ctx, "dashboards.database.Count")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
@ -690,16 +659,6 @@ func (d *dashboardStore) CleanupAfterDelete(ctx context.Context, cmd *dashboards
|
||||||
return err
|
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
|
// 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 {
|
func (d *dashboardStore) deleteResourcePermissions(sess *db.Session, orgID int64, resourceScope string) error {
|
||||||
// retrieve all permissions for the resource scope and org id
|
// 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
|
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) {
|
func (d *dashboardStore) FindDashboards(ctx context.Context, query *dashboards.FindPersistedDashboardsQuery) ([]dashboards.DashboardSearchProjection, error) {
|
||||||
ctx, span := tracer.Start(ctx, "dashboards.database.FindDashboards")
|
ctx, span := tracer.Start(ctx, "dashboards.database.FindDashboards")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
@ -1025,39 +932,6 @@ func (d *dashboardStore) GetDashboardTags(ctx context.Context, query *dashboards
|
||||||
return queryResult, nil
|
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(
|
func (d *dashboardStore) DeleteDashboardsInFolders(
|
||||||
ctx context.Context, req *dashboards.DeleteDashboardsInFolderRequest) error {
|
ctx context.Context, req *dashboards.DeleteDashboardsInFolderRequest) error {
|
||||||
ctx, span := tracer.Start(ctx, "dashboards.database.DeleteDashboardsInFolders")
|
ctx, span := tracer.Start(ctx, "dashboards.database.DeleteDashboardsInFolders")
|
||||||
|
|
|
@ -59,42 +59,6 @@ func TestIntegrationDashboardProvisioningTest(t *testing.T) {
|
||||||
require.NotEqual(t, 0, dash.ID)
|
require.NotEqual(t, 0, dash.ID)
|
||||||
dashId := 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) {
|
t.Run("Can query for provisioned dashboards", func(t *testing.T) {
|
||||||
rslt, err := dashboardStore.GetProvisionedDashboardData(context.Background(), "default")
|
rslt, err := dashboardStore.GetProvisionedDashboardData(context.Background(), "default")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
|
@ -10,27 +10,19 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/expr"
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"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/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
|
||||||
libmodel "github.com/grafana/grafana/pkg/services/libraryelements/model"
|
libmodel "github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/search/model"
|
"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/sqlstore/searchstore"
|
||||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"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/tests/testsuite"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -185,15 +177,6 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
||||||
require.False(t, queryResult.IsFolder)
|
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) {
|
t.Run("Shouldn't be able to get a dashboard with just an OrgID", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
query := dashboards.GetDashboardQuery{
|
query := dashboards.GetDashboardQuery{
|
||||||
|
@ -216,19 +199,6 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
|
||||||
require.Error(t, err, dashboards.ErrDashboardNotFound)
|
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) {
|
t.Run("Should be able to delete dashboard and associated tags", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
dash := insertTestDashboard(t, dashboardStore, "delete me", 1, 0, "", false, "delete this")
|
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)
|
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) {
|
t.Run("Should be able to get all dashboards for an org", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
dash1 := insertTestDashboard(t, dashboardStore, "org3test1", 3, 0, "", false, "org 1 test 1")
|
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)
|
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) {
|
t.Run("Can delete dashboards in folder", func(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
folder := insertTestDashboard(t, dashboardStore, "dash folder", 1, 0, "", true, "prod", "webapp")
|
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}})
|
err := dashboardStore.DeleteDashboardsInFolders(context.Background(), &dashboards.DeleteDashboardsInFolderRequest{OrgID: folder.OrgID, FolderUIDs: []string{folder.UID}})
|
||||||
require.NoError(t, err)
|
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)
|
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) {
|
func TestIntegrationFindDashboardsByTitle(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skipping integration test")
|
t.Skip("skipping integration test")
|
||||||
|
@ -844,13 +821,6 @@ func TestIntegrationFindDashboardsByTitle(t *testing.T) {
|
||||||
orgID := int64(1)
|
orgID := int64(1)
|
||||||
insertTestDashboard(t, dashboardStore, "dashboard under general", orgID, 0, "", false, []string{"tag1", "tag2"})
|
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{
|
user := &user.SignedInUser{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
Permissions: map[int64]map[string][]string{
|
Permissions: map[int64]map[string][]string{
|
||||||
|
@ -863,23 +833,10 @@ func TestIntegrationFindDashboardsByTitle(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
f0, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
f0 := insertTestFolder(t, dashboardStore, sqlStore, "f0", orgID, "")
|
||||||
OrgID: orgID,
|
|
||||||
Title: "f0",
|
|
||||||
SignedInUser: user,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
insertTestDashboard(t, dashboardStore, "dashboard under f0", orgID, 0, f0.UID, false, []string{"tag3"})
|
insertTestDashboard(t, dashboardStore, "dashboard under f0", orgID, 0, f0.UID, false, []string{"tag3"})
|
||||||
|
|
||||||
subfolder, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
subfolder := insertTestFolder(t, dashboardStore, sqlStore, "subfolder", orgID, f0.UID)
|
||||||
OrgID: orgID,
|
|
||||||
Title: "subfolder",
|
|
||||||
ParentUID: f0.UID,
|
|
||||||
SignedInUser: user,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
insertTestDashboard(t, dashboardStore, "dashboard under subfolder", orgID, 0, subfolder.UID, false)
|
insertTestDashboard(t, dashboardStore, "dashboard under subfolder", orgID, 0, subfolder.UID, false)
|
||||||
|
|
||||||
type res struct {
|
type res struct {
|
||||||
|
@ -981,14 +938,6 @@ func TestIntegrationFindDashboardsByFolder(t *testing.T) {
|
||||||
orgID := int64(1)
|
orgID := int64(1)
|
||||||
insertTestDashboard(t, dashboardStore, "dashboard under general", orgID, 0, "", false)
|
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{
|
user := &user.SignedInUser{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
Permissions: map[int64]map[string][]string{
|
Permissions: map[int64]map[string][]string{
|
||||||
|
@ -1001,31 +950,13 @@ func TestIntegrationFindDashboardsByFolder(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
f0, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
f0 := insertTestFolder(t, dashboardStore, sqlStore, "f0", orgID, "")
|
||||||
OrgID: orgID,
|
insertTestDashboard(t, dashboardStore, "dashboard under f0", orgID, 0, f0.UID, false)
|
||||||
Title: "f0",
|
|
||||||
SignedInUser: user,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
// nolint:staticcheck
|
|
||||||
insertTestDashboard(t, dashboardStore, "dashboard under f0", orgID, f0.ID, f0.UID, false)
|
|
||||||
|
|
||||||
f1, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
f1 := insertTestFolder(t, dashboardStore, sqlStore, "f1", orgID, "")
|
||||||
OrgID: orgID,
|
insertTestDashboard(t, dashboardStore, "dashboard under f1", orgID, 0, f1.UID, false)
|
||||||
Title: "f1",
|
|
||||||
SignedInUser: user,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
// nolint:staticcheck
|
|
||||||
insertTestDashboard(t, dashboardStore, "dashboard under f1", orgID, f1.ID, f1.UID, false)
|
|
||||||
|
|
||||||
subfolder, err := folderServiceWithFlagOn.Create(context.Background(), &folder.CreateFolderCommand{
|
subfolder := insertTestFolder(t, dashboardStore, sqlStore, "subfolder", orgID, f0.UID)
|
||||||
OrgID: orgID,
|
|
||||||
Title: "subfolder",
|
|
||||||
ParentUID: f0.UID,
|
|
||||||
SignedInUser: user,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
type res struct {
|
type res struct {
|
||||||
title string
|
title string
|
||||||
|
|
|
@ -150,10 +150,6 @@ func (dr *DashboardServiceImpl) cleanupK8sDashboardResources(ctx context.Context
|
||||||
ctx, span := tracer.Start(ctx, "dashboards.service.cleanupK8sDashboardResources")
|
ctx, span := tracer.Start(ctx, "dashboards.service.cleanupK8sDashboardResources")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
if !dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
readingFromLegacy := dualwrite.IsReadingLegacyDashboardsAndFolders(ctx, dr.dual)
|
readingFromLegacy := dualwrite.IsReadingLegacyDashboardsAndFolders(ctx, dr.dual)
|
||||||
if readingFromLegacy {
|
if readingFromLegacy {
|
||||||
// Legacy does its own cleanup
|
// 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) {
|
func (dr *DashboardServiceImpl) Count(ctx context.Context, scopeParams *quota.ScopeParameters) (*quota.Map, error) {
|
||||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
u := "a.Map{}
|
u := "a.Map{}
|
||||||
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -478,11 +473,8 @@ func (dr *DashboardServiceImpl) Count(ctx context.Context, scopeParams *quota.Sc
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr.dashboardStore.Count(ctx, scopeParams)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dr *DashboardServiceImpl) GetDashboardsByLibraryPanelUID(ctx context.Context, libraryPanelUID string, orgID int64) ([]*dashboards.DashboardRef, error) {
|
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{
|
res, err := dr.k8sclient.Search(ctx, orgID, &resourcepb.ResourceSearchRequest{
|
||||||
Options: &resourcepb.ListOptions{
|
Options: &resourcepb.ListOptions{
|
||||||
Fields: []*resourcepb.Requirement{
|
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) {
|
func (dr *DashboardServiceImpl) CountDashboardsInOrg(ctx context.Context, orgID int64) (int64, error) {
|
||||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
resp, err := dr.k8sclient.GetStats(ctx, orgID)
|
resp, err := dr.k8sclient.GetStats(ctx, orgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -532,9 +523,6 @@ func (dr *DashboardServiceImpl) CountDashboardsInOrg(ctx context.Context, orgID
|
||||||
return resp.Stats[0].Count, nil
|
return resp.Stats[0].Count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr.dashboardStore.CountInOrg(ctx, orgID, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
|
func readQuotaConfig(cfg *setting.Cfg) (*quota.Map, error) {
|
||||||
limits := "a.Map{}
|
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) {
|
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{})
|
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -582,11 +569,7 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardData(ctx context.Context,
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr.dashboardStore.GetProvisionedDashboardData(ctx, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardID(ctx context.Context, dashboardID int64) (*dashboards.DashboardProvisioning, error) {
|
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 dashboard id is 0, it is a new dashboard
|
||||||
if dashboardID == 0 {
|
if dashboardID == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -617,25 +600,7 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardID(ctx con
|
||||||
return nil, nil
|
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) {
|
func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardUID(ctx context.Context, orgID int64, dashboardUID string) (*dashboards.DashboardProvisioning, error) {
|
||||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
if dashboardUID == "" {
|
if dashboardUID == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -658,23 +623,6 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardUID(ctx co
|
||||||
return nil, nil
|
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 {
|
func (dr *DashboardServiceImpl) ValidateBasicDashboardProperties(title string, uid string, message string) error {
|
||||||
if title == "" {
|
if title == "" {
|
||||||
return dashboards.ErrDashboardTitleEmpty
|
return dashboards.ErrDashboardTitleEmpty
|
||||||
|
@ -730,7 +678,7 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate folder
|
// 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{
|
folder, err := dr.folderService.Get(ctx, &folder.GetFolderQuery{
|
||||||
OrgID: dash.OrgID,
|
OrgID: dash.OrgID,
|
||||||
UID: &dash.FolderUID,
|
UID: &dash.FolderUID,
|
||||||
|
@ -744,22 +692,6 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
|
||||||
// nolint:staticcheck
|
// nolint:staticcheck
|
||||||
dash.FolderID = folder.ID
|
dash.FolderID = folder.ID
|
||||||
dash.FolderUID = folder.UID
|
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)
|
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 {
|
func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *dashboards.DeleteOrphanedProvisionedDashboardsCommand) error {
|
||||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
// check each org for orphaned provisioned dashboards
|
// check each org for orphaned provisioned dashboards
|
||||||
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -983,9 +914,6 @@ func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr.dashboardStore.DeleteOrphanedProvisionedDashboards(ctx, cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dr *DashboardServiceImpl) ValidateDashboardRefreshInterval(minRefreshInterval string, targetRefreshInterval string) error {
|
func (dr *DashboardServiceImpl) ValidateDashboardRefreshInterval(minRefreshInterval string, targetRefreshInterval string) error {
|
||||||
if minRefreshInterval == "" {
|
if minRefreshInterval == "" {
|
||||||
return nil
|
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")
|
return nil, fmt.Errorf("failed to build save dashboard command. cmd is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
var dash *dashboards.Dashboard
|
dash, err := dr.saveProvisionedDashboardThroughK8s(ctx, cmd, provisioning, false)
|
||||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
dash, err = dr.saveProvisionedDashboardThroughK8s(ctx, cmd, provisioning, false)
|
|
||||||
} else {
|
|
||||||
dash, err = dr.dashboardStore.SaveProvisionedDashboard(ctx, *cmd, provisioning)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1065,10 +987,6 @@ func (dr *DashboardServiceImpl) SaveFolderForProvisionedDashboards(ctx context.C
|
||||||
return nil, err
|
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
|
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) {
|
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.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
|
// 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.
|
// 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 {
|
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.
|
// DeleteAllDashboards will delete all dashboards within a given org.
|
||||||
func (dr *DashboardServiceImpl) DeleteAllDashboards(ctx context.Context, orgId int64) error {
|
func (dr *DashboardServiceImpl) DeleteAllDashboards(ctx context.Context, orgId int64) error {
|
||||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return dr.deleteAllDashboardThroughK8s(ctx, orgId)
|
return dr.deleteAllDashboardThroughK8s(ctx, orgId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr.dashboardStore.DeleteAllDashboards(ctx, orgId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dr *DashboardServiceImpl) GetDashboardByPublicUid(ctx context.Context, dashboardPublicUid string) (*dashboards.Dashboard, error) {
|
func (dr *DashboardServiceImpl) GetDashboardByPublicUid(ctx context.Context, dashboardPublicUid string) (*dashboards.Dashboard, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -1141,30 +1051,9 @@ func (dr *DashboardServiceImpl) deleteDashboard(ctx context.Context, dashboardId
|
||||||
|
|
||||||
cmd := &dashboards.DeleteDashboardCommand{OrgID: orgId, ID: dashboardId, UID: dashboardUID}
|
cmd := &dashboards.DeleteDashboardCommand{OrgID: orgId, ID: dashboardId, UID: dashboardUID}
|
||||||
|
|
||||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return dr.deleteDashboardThroughK8s(ctx, cmd, validateProvisionedDashboard)
|
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) (
|
func (dr *DashboardServiceImpl) ImportDashboard(ctx context.Context, dto *dashboards.SaveDashboardDTO) (
|
||||||
*dashboards.Dashboard, error) {
|
*dashboards.Dashboard, error) {
|
||||||
ctx, span := tracer.Start(ctx, "dashboards.service.ImportDashboard")
|
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
|
// UnprovisionDashboard removes info about dashboard being provisioned. Used after provisioning configs are changed
|
||||||
// and provisioned dashboards are left behind but not deleted.
|
// and provisioned dashboards are left behind but not deleted.
|
||||||
func (dr *DashboardServiceImpl) UnprovisionDashboard(ctx context.Context, dashboardId int64) error {
|
func (dr *DashboardServiceImpl) UnprovisionDashboard(ctx context.Context, dashboardId int64) error {
|
||||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
orgs, err := dr.orgService.Search(ctx, &org.SearchOrgsQuery{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1227,11 +1115,7 @@ func (dr *DashboardServiceImpl) UnprovisionDashboard(ctx context.Context, dashbo
|
||||||
return dashboards.ErrDashboardNotFound
|
return dashboards.ErrDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr.dashboardStore.UnprovisionDashboard(ctx, dashboardId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dr *DashboardServiceImpl) GetDashboardsByPluginID(ctx context.Context, query *dashboards.GetDashboardsByPluginIDQuery) ([]*dashboards.Dashboard, error) {
|
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{
|
dashs, err := dr.searchDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{
|
||||||
OrgId: query.OrgID,
|
OrgId: query.OrgID,
|
||||||
ManagedBy: utils.ManagerKindPlugin,
|
ManagedBy: utils.ManagerKindPlugin,
|
||||||
|
@ -1253,8 +1137,6 @@ func (dr *DashboardServiceImpl) GetDashboardsByPluginID(ctx context.Context, que
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
return dr.dashboardStore.GetDashboardsByPluginID(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// (sometimes) called by the k8s storage engine after creating an object
|
// (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 {
|
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) {
|
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.getDashboardThroughK8s(ctx, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr.dashboardStore.GetDashboard(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dr *DashboardServiceImpl) GetDashboardUIDByID(ctx context.Context, query *dashboards.GetDashboardRefByIDQuery) (*dashboards.DashboardRef, error) {
|
func (dr *DashboardServiceImpl) GetDashboardUIDByID(ctx context.Context, query *dashboards.GetDashboardRefByIDQuery) (*dashboards.DashboardRef, error) {
|
||||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
requester, err := identity.GetRequester(ctx)
|
requester, err := identity.GetRequester(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 &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
|
// 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) {
|
func (dr *DashboardServiceImpl) GetDashboards(ctx context.Context, query *dashboards.GetDashboardsQuery) ([]*dashboards.Dashboard, error) {
|
||||||
if dr.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
if query.OrgID == 0 {
|
if query.OrgID == 0 {
|
||||||
requester, err := identity.GetRequester(ctx)
|
requester, err := identity.GetRequester(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1446,9 +1301,6 @@ func (dr *DashboardServiceImpl) GetDashboards(ctx context.Context, query *dashbo
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr.dashboardStore.GetDashboards(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dr *DashboardServiceImpl) getDashboardsSharedWithUser(ctx context.Context, user identity.Requester) ([]*dashboards.DashboardRef, error) {
|
func (dr *DashboardServiceImpl) getDashboardsSharedWithUser(ctx context.Context, user identity.Requester) ([]*dashboards.DashboardRef, error) {
|
||||||
ctx, span := tracer.Start(ctx, "dashboards.service.getDashboardsSharedWithUser")
|
ctx, span := tracer.Start(ctx, "dashboards.service.getDashboardsSharedWithUser")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
@ -1468,21 +1320,10 @@ func (dr *DashboardServiceImpl) getDashboardsSharedWithUser(ctx context.Context,
|
||||||
return []*dashboards.DashboardRef{}, nil
|
return []*dashboards.DashboardRef{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboardsQuery := &dashboards.GetDashboardsQuery{
|
dashs, err := dr.searchDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{
|
||||||
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{
|
|
||||||
DashboardUIDs: dashboardUids,
|
DashboardUIDs: dashboardUids,
|
||||||
OrgId: user.GetOrgID(),
|
OrgId: user.GetOrgID(),
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
dashs, err = dr.dashboardStore.GetDashboards(ctx, dashboardsQuery)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1573,7 +1414,6 @@ func (dr *DashboardServiceImpl) FindDashboards(ctx context.Context, query *dashb
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
}
|
}
|
||||||
|
|
||||||
if dr.features.IsEnabled(ctx, featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
if query.OrgId == 0 {
|
if query.OrgId == 0 {
|
||||||
requester, err := identity.GetRequester(ctx)
|
requester, err := identity.GetRequester(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1633,9 +1473,6 @@ func (dr *DashboardServiceImpl) FindDashboards(ctx context.Context, query *dashb
|
||||||
return finalResults, nil
|
return finalResults, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr.dashboardStore.FindDashboards(ctx, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
type folderRes struct {
|
type folderRes struct {
|
||||||
Title string
|
Title string
|
||||||
ID int64
|
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) {
|
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.listDashboardsThroughK8s(ctx, orgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return dr.dashboardStore.GetAllDashboardsByOrgId(ctx, orgID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHitType(item dashboards.DashboardSearchProjection) model.HitType {
|
func getHitType(item dashboards.DashboardSearchProjection) model.HitType {
|
||||||
var hitType model.HitType
|
var hitType model.HitType
|
||||||
if item.IsFolder {
|
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) {
|
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{
|
res, err := dr.k8sclient.Search(ctx, query.OrgID, &resourcepb.ResourceSearchRequest{
|
||||||
Facet: map[string]*resourcepb.ResourceSearchRequest_Facet{
|
Facet: map[string]*resourcepb.ResourceSearchRequest_Facet{
|
||||||
"tags": {
|
"tags": {
|
||||||
|
@ -1771,11 +1603,7 @@ func (dr *DashboardServiceImpl) GetDashboardTags(ctx context.Context, query *das
|
||||||
return results, nil
|
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) {
|
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{
|
dashs, err := dr.searchDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{
|
||||||
OrgId: orgID,
|
OrgId: orgID,
|
||||||
FolderUIDs: folderUIDs,
|
FolderUIDs: folderUIDs,
|
||||||
|
@ -1787,9 +1615,6 @@ func (dr DashboardServiceImpl) CountInFolders(ctx context.Context, orgID int64,
|
||||||
return int64(len(dashs)), nil
|
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 {
|
func (dr *DashboardServiceImpl) DeleteInFolders(ctx context.Context, orgID int64, folderUIDs []string, u identity.Requester) error {
|
||||||
ctx, span := tracer.Start(ctx, "dashboards.service.DeleteInFolders")
|
ctx, span := tracer.Start(ctx, "dashboards.service.DeleteInFolders")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
|
@ -54,25 +54,24 @@ func TestMain(m *testing.M) {
|
||||||
testsuite.Run(m)
|
testsuite.Run(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDashboardService(t *testing.T) {
|
func TestDashboardServiceValidation(t *testing.T) {
|
||||||
t.Run("Dashboard service tests", func(t *testing.T) {
|
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
fakeStore := dashboards.FakeDashboardStore{}
|
||||||
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
||||||
defer fakeStore.AssertExpectations(t)
|
defer fakeStore.AssertExpectations(t)
|
||||||
|
|
||||||
folderSvc := foldertest.NewFakeService()
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
log: log.New("test.logger"),
|
log: log.New("test.logger"),
|
||||||
dashboardStore: &fakeStore,
|
dashboardStore: &fakeStore,
|
||||||
folderService: folderSvc,
|
folderService: foldertest.NewFakeService(),
|
||||||
ac: actest.FakeAccessControl{ExpectedEvaluate: true},
|
ac: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||||
features: featuremgmt.WithFeatures(),
|
features: featuremgmt.WithFeatures(),
|
||||||
publicDashboardService: fakePublicDashboardService,
|
publicDashboardService: fakePublicDashboardService,
|
||||||
}
|
}
|
||||||
folderStore := foldertest.FakeFolderStore{}
|
|
||||||
folderStore.On("GetFolderByUID", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).Return(nil, dashboards.ErrFolderNotFound).Once()
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
service.folderStore = &folderStore
|
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) {
|
t.Run("Save dashboard validation", func(t *testing.T) {
|
||||||
dto := &dashboards.SaveDashboardDTO{}
|
dto := &dashboards.SaveDashboardDTO{}
|
||||||
|
@ -82,7 +81,7 @@ func TestDashboardService(t *testing.T) {
|
||||||
|
|
||||||
for _, title := range titles {
|
for _, title := range titles {
|
||||||
dto.Dashboard = dashboards.NewDashboard(title)
|
dto.Dashboard = dashboards.NewDashboard(title)
|
||||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
_, err := service.SaveDashboard(ctx, dto, false)
|
||||||
require.Equal(t, err, dashboards.ErrDashboardTitleEmpty)
|
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:
|
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
|
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
|
this test passes or if the result is more exciting than these 500 characters
|
||||||
I wrote. Best of luck to the both of us!`
|
I wrote. Best of luck to the both of us!!`
|
||||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
_, err := service.SaveDashboard(ctx, dto, false)
|
||||||
require.Equal(t, err, dashboards.ErrDashboardMessageTooLong)
|
require.Equal(t, err, dashboards.ErrDashboardMessageTooLong)
|
||||||
|
|
||||||
// set to a shorter message for the rest of the tests
|
// 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) {
|
t.Run("Should return validation error if folder is named General", func(t *testing.T) {
|
||||||
dto.Dashboard = dashboards.NewDashboardFolder("General")
|
dto.Dashboard = dashboards.NewDashboardFolder("General")
|
||||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
_, err := service.SaveDashboard(ctx, dto, false)
|
||||||
require.Equal(t, err, dashboards.ErrDashboardFolderNameExists)
|
require.Equal(t, err, dashboards.ErrDashboardFolderNameExists)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -129,9 +128,9 @@ func TestDashboardService(t *testing.T) {
|
||||||
dto.User = &user.SignedInUser{}
|
dto.User = &user.SignedInUser{}
|
||||||
|
|
||||||
if tc.Error == nil {
|
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)
|
require.Equal(t, err, tc.Error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -141,137 +140,15 @@ func TestDashboardService(t *testing.T) {
|
||||||
dto.Dashboard.FolderUID = "non-existing-folder"
|
dto.Dashboard.FolderUID = "non-existing-folder"
|
||||||
folderSvc := foldertest.FakeService{ExpectedError: dashboards.ErrFolderNotFound}
|
folderSvc := foldertest.FakeService{ExpectedError: dashboards.ErrFolderNotFound}
|
||||||
service.folderService = &folderSvc
|
service.folderService = &folderSvc
|
||||||
_, err := service.SaveDashboard(context.Background(), dto, false)
|
_, err := service.SaveDashboard(ctx, dto, false)
|
||||||
require.Equal(t, err, dashboards.ErrFolderNotFound)
|
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) {
|
func setupK8sDashboardTests(service *DashboardServiceImpl) (context.Context, *client.MockK8sHandler) {
|
||||||
mockCli := new(client.MockK8sHandler)
|
mockCli := new(client.MockK8sHandler)
|
||||||
service.k8sclient = mockCli
|
service.k8sclient = mockCli
|
||||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders)
|
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
userCtx := &user.SignedInUser{UserID: 1, OrgID: 1}
|
userCtx := &user.SignedInUser{UserID: 1, OrgID: 1}
|
||||||
|
@ -281,27 +158,15 @@ func setupK8sDashboardTests(service *DashboardServiceImpl) (context.Context, *cl
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetDashboard(t *testing.T) {
|
func TestGetDashboard(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
}
|
}
|
||||||
query := &dashboards.GetDashboardQuery{
|
query := &dashboards.GetDashboardQuery{
|
||||||
UID: "test-uid",
|
UID: "test-uid",
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
t.Run("Should get dashboard", 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) {
|
|
||||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
||||||
"metadata": map[string]any{
|
"metadata": map[string]any{
|
||||||
|
@ -421,23 +286,9 @@ func TestGetDashboard(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAllDashboards(t *testing.T) {
|
func TestGetAllDashboards(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
|
|
||||||
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
||||||
|
@ -469,27 +320,12 @@ func TestGetAllDashboards(t *testing.T) {
|
||||||
k8sCliMock.AssertExpectations(t)
|
k8sCliMock.AssertExpectations(t)
|
||||||
// make sure the conversion is working
|
// make sure the conversion is working
|
||||||
require.True(t, reflect.DeepEqual(dashes, []*dashboards.Dashboard{&dashboardExpected}))
|
require.True(t, reflect.DeepEqual(dashes, []*dashboards.Dashboard{&dashboardExpected}))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAllDashboardsByOrgId(t *testing.T) {
|
func TestGetAllDashboardsByOrgId(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
|
|
||||||
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
||||||
|
@ -521,30 +357,16 @@ func TestGetAllDashboardsByOrgId(t *testing.T) {
|
||||||
k8sCliMock.AssertExpectations(t)
|
k8sCliMock.AssertExpectations(t)
|
||||||
// make sure the conversion is working
|
// make sure the conversion is working
|
||||||
require.True(t, reflect.DeepEqual(dashes, []*dashboards.Dashboard{&dashboardExpected}))
|
require.True(t, reflect.DeepEqual(dashes, []*dashboards.Dashboard{&dashboardExpected}))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetProvisionedDashboardData(t *testing.T) {
|
func TestGetProvisionedDashboardData(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
orgService: &orgtest.FakeOrgService{
|
orgService: &orgtest.FakeOrgService{
|
||||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
provisioningTimestamp := int64(1234567)
|
provisioningTimestamp := int64(1234567)
|
||||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||||
|
@ -634,30 +456,16 @@ func TestGetProvisionedDashboardData(t *testing.T) {
|
||||||
Updated: provisioningTimestamp,
|
Updated: provisioningTimestamp,
|
||||||
})
|
})
|
||||||
k8sCliMock.AssertExpectations(t)
|
k8sCliMock.AssertExpectations(t)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetProvisionedDashboardDataByDashboardID(t *testing.T) {
|
func TestGetProvisionedDashboardDataByDashboardID(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
orgService: &orgtest.FakeOrgService{
|
orgService: &orgtest.FakeOrgService{
|
||||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
provisioningTimestamp := int64(1234567)
|
provisioningTimestamp := int64(1234567)
|
||||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||||
|
@ -736,30 +544,15 @@ func TestGetProvisionedDashboardDataByDashboardID(t *testing.T) {
|
||||||
Updated: provisioningTimestamp,
|
Updated: provisioningTimestamp,
|
||||||
})
|
})
|
||||||
k8sCliMock.AssertExpectations(t)
|
k8sCliMock.AssertExpectations(t)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetProvisionedDashboardDataByDashboardUID(t *testing.T) {
|
func TestGetProvisionedDashboardDataByDashboardUID(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
orgService: &orgtest.FakeOrgService{
|
orgService: &orgtest.FakeOrgService{
|
||||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
provisioningTimestamp := int64(1234567)
|
provisioningTimestamp := int64(1234567)
|
||||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||||
|
@ -831,16 +624,12 @@ func TestGetProvisionedDashboardDataByDashboardUID(t *testing.T) {
|
||||||
Updated: provisioningTimestamp,
|
Updated: provisioningTimestamp,
|
||||||
})
|
})
|
||||||
k8sCliMock.AssertExpectations(t)
|
k8sCliMock.AssertExpectations(t)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
|
func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
orgService: &orgtest.FakeOrgService{
|
orgService: &orgtest.FakeOrgService{
|
||||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
ExpectedOrgs: []*org.OrgDTO{{ID: 1}, {ID: 2}},
|
||||||
},
|
},
|
||||||
|
@ -848,19 +637,7 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
|
||||||
log: log.NewNopLogger(),
|
log: log.NewNopLogger(),
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Should fallback to dashboard store if Kubernetes feature flags are not enabled", func(t *testing.T) {
|
t.Run("Should delete across all orgs, but only delete file based provisioned dashboards", 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) {
|
|
||||||
_, k8sCliMock := setupK8sDashboardTests(service)
|
_, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||||
k8sCliMock.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
k8sCliMock.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||||
|
@ -1008,7 +785,6 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
|
||||||
repo := "test"
|
repo := "test"
|
||||||
singleOrgService := &DashboardServiceImpl{
|
singleOrgService := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
orgService: &orgtest.FakeOrgService{
|
orgService: &orgtest.FakeOrgService{
|
||||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}},
|
ExpectedOrgs: []*org.OrgDTO{{ID: 1}},
|
||||||
},
|
},
|
||||||
|
@ -1102,7 +878,6 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
|
||||||
repo := "test"
|
repo := "test"
|
||||||
singleOrgService := &DashboardServiceImpl{
|
singleOrgService := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
orgService: &orgtest.FakeOrgService{
|
orgService: &orgtest.FakeOrgService{
|
||||||
ExpectedOrgs: []*org.OrgDTO{{ID: 1}},
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
dash := &unstructured.Unstructured{Object: map[string]any{
|
dash := &unstructured.Unstructured{Object: map[string]any{
|
||||||
"metadata": map[string]any{
|
"metadata": map[string]any{
|
||||||
|
@ -1216,17 +982,12 @@ func TestUnprovisionDashboard(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
k8sCliMock.AssertExpectations(t)
|
k8sCliMock.AssertExpectations(t)
|
||||||
fakeStore.AssertExpectations(t)
|
fakeStore.AssertExpectations(t)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetDashboardsByPluginID(t *testing.T) {
|
func TestGetDashboardsByPluginID(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
query := &dashboards.GetDashboardsByPluginIDQuery{
|
query := &dashboards.GetDashboardsByPluginIDQuery{
|
||||||
PluginID: "testing",
|
PluginID: "testing",
|
||||||
OrgID: 1,
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||||
k8sCliMock.On("Get", mock.Anything, "uid", mock.Anything, mock.Anything, mock.Anything).Return(uidUnstructured, nil)
|
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.NoError(t, err)
|
||||||
require.Len(t, dashes, 1)
|
require.Len(t, dashes, 1)
|
||||||
k8sCliMock.AssertExpectations(t)
|
k8sCliMock.AssertExpectations(t)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetDefaultPermissionsWhenSavingFolderForProvisionedDashboards(t *testing.T) {
|
func TestSetDefaultPermissionsWhenSavingFolderForProvisionedDashboards(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
fakeStore := dashboards.FakeDashboardStore{}
|
||||||
defer fakeStore.AssertExpectations(t)
|
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 := acmock.NewMockedPermissionsService()
|
||||||
folderPermService.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
|
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,
|
OrgID: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
service.features = featuremgmt.WithFeatures(tc.featuresArr...)
|
service.features = featuremgmt.WithFeatures()
|
||||||
folder, err := service.SaveFolderForProvisionedDashboards(context.Background(), cmd)
|
folder, err := service.SaveFolderForProvisionedDashboards(context.Background(), cmd)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, folder)
|
require.NotNil(t, folder)
|
||||||
|
|
||||||
folderPermService.AssertNumberOfCalls(t, "SetPermissions", tc.expectedCallsToSetPermissions)
|
folderPermService.AssertNumberOfCalls(t, "SetPermissions", 0)
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSaveProvisionedDashboard(t *testing.T) {
|
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"}),
|
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{
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
||||||
"metadata": map[string]any{
|
"metadata": map[string]any{
|
||||||
"name": "uid",
|
"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)
|
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("GetUsersFromMeta", mock.Anything, mock.Anything).Return(map[string]*user.User{}, nil)
|
||||||
k8sCliMock.On("Update", mock.Anything, mock.Anything, mock.Anything, metav1.UpdateOptions{
|
k8sCliMock.On("Update", mock.Anything, mock.Anything, mock.Anything, metav1.UpdateOptions{
|
||||||
FieldValidation: metav1.FieldValidationIgnore,
|
FieldValidation: metav1.FieldValidationIgnore,
|
||||||
|
@ -1419,7 +1136,6 @@ func TestSaveProvisionedDashboard(t *testing.T) {
|
||||||
k8sCliMock.AssertExpectations(t)
|
k8sCliMock.AssertExpectations(t)
|
||||||
// ensure the provisioning data is still saved to the db
|
// ensure the provisioning data is still saved to the db
|
||||||
fakeStore.AssertExpectations(t)
|
fakeStore.AssertExpectations(t)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSaveDashboard(t *testing.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{
|
dashboardUnstructured := unstructured.Unstructured{Object: map[string]any{
|
||||||
"metadata": map[string]any{
|
"metadata": map[string]any{
|
||||||
"name": "uid",
|
"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) {
|
t.Run("Should use Kubernetes create if feature flags are enabled and dashboard doesn't exist", func(t *testing.T) {
|
||||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
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("GetUsersFromMeta", mock.Anything, mock.Anything).Return(map[string]*user.User{}, nil)
|
||||||
k8sCliMock.On("GetNamespace", mock.Anything).Return("default")
|
k8sCliMock.On("GetNamespace", mock.Anything).Return("default")
|
||||||
k8sCliMock.On("Update", mock.Anything, mock.Anything, mock.Anything, metav1.UpdateOptions{
|
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) {
|
t.Run("Should use Kubernetes update if feature flags are enabled and dashboard exists", func(t *testing.T) {
|
||||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
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("GetUsersFromMeta", mock.Anything, mock.Anything).Return(map[string]*user.User{}, nil)
|
||||||
k8sCliMock.On("GetNamespace", mock.Anything).Return("default")
|
k8sCliMock.On("GetNamespace", mock.Anything).Return("default")
|
||||||
k8sCliMock.On("Update", mock.Anything, mock.Anything, mock.Anything, metav1.UpdateOptions{
|
k8sCliMock.On("Update", mock.Anything, mock.Anything, mock.Anything, metav1.UpdateOptions{
|
||||||
|
@ -1517,17 +1224,6 @@ func TestDeleteDashboard(t *testing.T) {
|
||||||
publicDashboardService: fakePublicDashboardService,
|
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) {
|
t.Run("Should use Kubernetes client if feature flags are enabled", func(t *testing.T) {
|
||||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
k8sCliMock.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
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,
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
k8sCliMock.On("DeleteCollection", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
k8sCliMock.On("DeleteCollection", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
|
||||||
|
|
||||||
err := service.DeleteAllDashboards(ctx, 1)
|
err := service.DeleteAllDashboards(ctx, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
k8sCliMock.AssertExpectations(t)
|
k8sCliMock.AssertExpectations(t)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearchDashboards(t *testing.T) {
|
func TestSearchDashboards(t *testing.T) {
|
||||||
|
@ -1612,6 +1298,7 @@ func TestSearchDashboards(t *testing.T) {
|
||||||
defer fakeStore.AssertExpectations(t)
|
defer fakeStore.AssertExpectations(t)
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
|
features: featuremgmt.WithFeatures(),
|
||||||
dashboardStore: &fakeStore,
|
dashboardStore: &fakeStore,
|
||||||
folderService: fakeFolders,
|
folderService: fakeFolders,
|
||||||
metrics: newDashboardsMetrics(prometheus.NewRegistry()),
|
metrics: newDashboardsMetrics(prometheus.NewRegistry()),
|
||||||
|
@ -1651,38 +1338,8 @@ func TestSearchDashboards(t *testing.T) {
|
||||||
query := dashboards.FindPersistedDashboardsQuery{
|
query := dashboards.FindPersistedDashboardsQuery{
|
||||||
DashboardUIDs: []string{"uid1", "uid2"},
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
expectedFolders := model.HitList{
|
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) {
|
t.Run("Should handle Shared with me folder correctly", func(t *testing.T) {
|
||||||
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
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("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||||
k8sCliMock.On("Search", mock.Anything, int64(1), mock.MatchedBy(func(req *resourcepb.ResourceSearchRequest) bool {
|
k8sCliMock.On("Search", mock.Anything, int64(1), mock.MatchedBy(func(req *resourcepb.ResourceSearchRequest) bool {
|
||||||
if len(req.Options.Fields) == 0 {
|
if len(req.Options.Fields) == 0 {
|
||||||
|
@ -1848,11 +1505,8 @@ func TestSearchDashboards(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetDashboards(t *testing.T) {
|
func TestGetDashboards(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedResult := []*dashboards.Dashboard{
|
expectedResult := []*dashboards.Dashboard{
|
||||||
|
@ -1899,25 +1553,7 @@ func TestGetDashboards(t *testing.T) {
|
||||||
DashboardUIDs: []string{"uid1", "uid2"},
|
DashboardUIDs: []string{"uid1", "uid2"},
|
||||||
OrgID: 1,
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||||
k8sCliMock.On("Get", mock.Anything, "uid1", mock.Anything, mock.Anything, mock.Anything).Return(uid1Unstructured, nil)
|
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.NoError(t, err)
|
||||||
require.Equal(t, expectedResult, result)
|
require.Equal(t, expectedResult, result)
|
||||||
k8sCliMock.AssertExpectations(t)
|
k8sCliMock.AssertExpectations(t)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetDashboardUIDByID(t *testing.T) {
|
func TestGetDashboardUIDByID(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedResult := &dashboards.DashboardRef{
|
expectedResult := &dashboards.DashboardRef{
|
||||||
|
@ -1991,17 +1623,6 @@ func TestGetDashboardUIDByID(t *testing.T) {
|
||||||
query := &dashboards.GetDashboardRefByIDQuery{
|
query := &dashboards.GetDashboardRefByIDQuery{
|
||||||
ID: 1,
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||||
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resourcepb.ResourceSearchResponse{
|
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.NoError(t, err)
|
||||||
require.Equal(t, expectedResult, result)
|
require.Equal(t, expectedResult, result)
|
||||||
k8sCliMock.AssertExpectations(t)
|
k8sCliMock.AssertExpectations(t)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnstructuredToLegacyDashboard(t *testing.T) {
|
func TestUnstructuredToLegacyDashboard(t *testing.T) {
|
||||||
|
@ -2092,11 +1712,8 @@ func TestUnstructuredToLegacyDashboard(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetDashboardTags(t *testing.T) {
|
func TestGetDashboardTags(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedResult := []*dashboards.DashboardTagCloudItem{
|
expectedResult := []*dashboards.DashboardTagCloudItem{
|
||||||
|
@ -2112,16 +1729,6 @@ func TestGetDashboardTags(t *testing.T) {
|
||||||
query := &dashboards.GetDashboardTagsQuery{
|
query := &dashboards.GetDashboardTagsQuery{
|
||||||
OrgID: 1,
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resourcepb.ResourceSearchResponse{
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resourcepb.ResourceSearchResponse{
|
||||||
Facet: map[string]*resourcepb.ResourceSearchResponse_Facet{
|
Facet: map[string]*resourcepb.ResourceSearchResponse_Facet{
|
||||||
|
@ -2143,16 +1750,11 @@ func TestGetDashboardTags(t *testing.T) {
|
||||||
result, err := service.GetDashboardTags(ctx, query)
|
result, err := service.GetDashboardTags(ctx, query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expectedResult, result)
|
require.Equal(t, expectedResult, result)
|
||||||
fakeStore.AssertExpectations(t)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestQuotaCount(t *testing.T) {
|
func TestQuotaCount(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
orgs := []*org.OrgDTO{
|
orgs := []*org.OrgDTO{
|
||||||
|
@ -2182,15 +1784,6 @@ func TestQuotaCount(t *testing.T) {
|
||||||
query := "a.ScopeParameters{
|
query := "a.ScopeParameters{
|
||||||
OrgID: 1,
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
orgSvc := orgtest.FakeOrgService{ExpectedOrgs: orgs}
|
orgSvc := orgtest.FakeOrgService{ExpectedOrgs: orgs}
|
||||||
service.orgService = &orgSvc
|
service.orgService = &orgSvc
|
||||||
|
@ -2209,17 +1802,11 @@ func TestQuotaCount(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
c, _ = result.Get(globalTag)
|
c, _ = result.Get(globalTag)
|
||||||
require.Equal(t, c, int64(3))
|
require.Equal(t, c, int64(3))
|
||||||
|
|
||||||
fakeStore.AssertExpectations(t)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCountDashboardsInOrg(t *testing.T) {
|
func TestCountDashboardsInOrg(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
}
|
}
|
||||||
count := resourcepb.ResourceStatsResponse{
|
count := resourcepb.ResourceStatsResponse{
|
||||||
Stats: []*resourcepb.ResourceStatsResponse_Stats{
|
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)
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
k8sCliMock.On("GetStats", mock.Anything, mock.Anything).Return(&count, nil).Once()
|
k8sCliMock.On("GetStats", mock.Anything, mock.Anything).Return(&count, nil).Once()
|
||||||
result, err := service.CountDashboardsInOrg(ctx, 1)
|
result, err := service.CountDashboardsInOrg(ctx, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, result, int64(3))
|
require.Equal(t, result, int64(3))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCountInFolders(t *testing.T) {
|
func TestCountInFolders(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
|
||||||
defer fakeStore.AssertExpectations(t)
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
dashboardStore: &fakeStore,
|
|
||||||
}
|
}
|
||||||
|
ctx, k8sCliMock := setupK8sDashboardTests(service)
|
||||||
dashs := &resourcepb.ResourceSearchResponse{
|
dashs := &resourcepb.ResourceSearchResponse{
|
||||||
Results: &resourcepb.ResourceTable{
|
Results: &resourcepb.ResourceTable{
|
||||||
Columns: []*resourcepb.ResourceTableColumnDefinition{
|
Columns: []*resourcepb.ResourceTableColumnDefinition{
|
||||||
|
@ -2291,22 +1866,11 @@ func TestCountInFolders(t *testing.T) {
|
||||||
TotalHits: 2,
|
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("GetNamespace", mock.Anything, mock.Anything).Return("default")
|
||||||
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(dashs, nil).Once()
|
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(dashs, nil).Once()
|
||||||
result, err := service.CountInFolders(ctx, 1, []string{"folder1"}, &user.SignedInUser{})
|
result, err := service.CountInFolders(ctx, 1, []string{"folder1"}, &user.SignedInUser{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, result, int64(2))
|
require.Equal(t, result, int64(2))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearchDashboardsThroughK8sRaw(t *testing.T) {
|
func TestSearchDashboardsThroughK8sRaw(t *testing.T) {
|
||||||
|
@ -2654,26 +2218,18 @@ func TestIntegrationK8sDashboardCleanupJob(t *testing.T) {
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
featureEnabled bool
|
|
||||||
readFromUnified bool
|
readFromUnified bool
|
||||||
batchSize int
|
batchSize int
|
||||||
setupFunc func(*DashboardServiceImpl, context.Context, *client.MockK8sHandler)
|
setupFunc func(*DashboardServiceImpl, context.Context, *client.MockK8sHandler)
|
||||||
verifyFunc func(*testing.T, *DashboardServiceImpl, context.Context, *client.MockK8sHandler, *kvstore.FakeKVStore)
|
verifyFunc func(*testing.T, *DashboardServiceImpl, context.Context, *client.MockK8sHandler, *kvstore.FakeKVStore)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Should not run cleanup when feature flag is disabled",
|
name: "Should not run cleanup when we're reading from legacy",
|
||||||
featureEnabled: false,
|
|
||||||
batchSize: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Should not run cleanup when feature flag is enabled but we're reading from legacy",
|
|
||||||
featureEnabled: true,
|
|
||||||
readFromUnified: false,
|
readFromUnified: false,
|
||||||
batchSize: 10,
|
batchSize: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Should process dashboard cleanup for all orgs",
|
name: "Should process dashboard cleanup for all orgs",
|
||||||
featureEnabled: true,
|
|
||||||
readFromUnified: true,
|
readFromUnified: true,
|
||||||
batchSize: 10,
|
batchSize: 10,
|
||||||
setupFunc: func(service *DashboardServiceImpl, ctx context.Context, k8sCliMock *client.MockK8sHandler) {
|
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",
|
name: "Should handle pagination and batching when processing large sets of dashboards",
|
||||||
featureEnabled: true,
|
|
||||||
readFromUnified: true,
|
readFromUnified: true,
|
||||||
batchSize: 3,
|
batchSize: 3,
|
||||||
setupFunc: func(service *DashboardServiceImpl, ctx context.Context, k8sCliMock *client.MockK8sHandler) {
|
setupFunc: func(service *DashboardServiceImpl, ctx context.Context, k8sCliMock *client.MockK8sHandler) {
|
||||||
|
@ -2836,11 +2391,7 @@ func TestIntegrationK8sDashboardCleanupJob(t *testing.T) {
|
||||||
fakeStore := dashboards.FakeDashboardStore{}
|
fakeStore := dashboards.FakeDashboardStore{}
|
||||||
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
fakePublicDashboardService := publicdashboards.NewFakePublicDashboardServiceWrapper(t)
|
||||||
fakeOrgService := orgtest.NewOrgServiceFake()
|
fakeOrgService := orgtest.NewOrgServiceFake()
|
||||||
|
|
||||||
features := featuremgmt.WithFeatures()
|
features := featuremgmt.WithFeatures()
|
||||||
if tc.featureEnabled {
|
|
||||||
features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders)
|
|
||||||
}
|
|
||||||
|
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: setting.NewCfg(),
|
cfg: setting.NewCfg(),
|
||||||
|
@ -2885,7 +2436,7 @@ func TestIntegrationK8sDashboardCleanupJob(t *testing.T) {
|
||||||
service := &DashboardServiceImpl{
|
service := &DashboardServiceImpl{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
log: log.New("test.logger"),
|
log: log.New("test.logger"),
|
||||||
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
|
features: featuremgmt.WithFeatures(),
|
||||||
serverLockService: lockService,
|
serverLockService: lockService,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2942,7 +2493,7 @@ func TestGetDashboardsByLibraryPanelUID(t *testing.T) {
|
||||||
dashboardStore: &fakeStore,
|
dashboardStore: &fakeStore,
|
||||||
folderService: folderSvc,
|
folderService: folderSvc,
|
||||||
ac: actest.FakeAccessControl{ExpectedEvaluate: true},
|
ac: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||||
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders, featuremgmt.FlagKubernetesLibraryPanelConnections),
|
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesLibraryPanelConnections),
|
||||||
publicDashboardService: fakePublicDashboardService,
|
publicDashboardService: fakePublicDashboardService,
|
||||||
k8sclient: k8sCliMock,
|
k8sclient: k8sCliMock,
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,34 +62,6 @@ func (_m *FakeDashboardStore) Count(_a0 context.Context, _a1 *quota.ScopeParamet
|
||||||
return r0, r1
|
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
|
// CountInOrg provides a mock function with given fields: ctx, orgID, isFolder
|
||||||
func (_m *FakeDashboardStore) CountInOrg(ctx context.Context, orgID int64, isFolder bool) (int64, error) {
|
func (_m *FakeDashboardStore) CountInOrg(ctx context.Context, orgID int64, isFolder bool) (int64, error) {
|
||||||
ret := _m.Called(ctx, orgID, isFolder)
|
ret := _m.Called(ctx, orgID, isFolder)
|
||||||
|
@ -118,23 +90,6 @@ func (_m *FakeDashboardStore) CountInOrg(ctx context.Context, orgID int64, isFol
|
||||||
return r0, r1
|
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
|
// DeleteDashboard provides a mock function with given fields: ctx, cmd
|
||||||
func (_m *FakeDashboardStore) DeleteDashboard(ctx context.Context, cmd *DeleteDashboardCommand) error {
|
func (_m *FakeDashboardStore) DeleteDashboard(ctx context.Context, cmd *DeleteDashboardCommand) error {
|
||||||
|
@ -172,23 +127,6 @@ func (_m *FakeDashboardStore) DeleteDashboardsInFolders(ctx context.Context, req
|
||||||
return r0
|
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
|
// FindDashboards provides a mock function with given fields: ctx, query
|
||||||
func (_m *FakeDashboardStore) FindDashboards(ctx context.Context, query *FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error) {
|
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
|
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
|
// GetDashboardsByLibraryPanelUID provides a mock function with given fields: ctx, libraryPanelUID, orgID
|
||||||
func (_m *FakeDashboardStore) GetDashboardsByLibraryPanelUID(ctx context.Context, libraryPanelUID string, orgID int64) ([]*DashboardRef, error) {
|
func (_m *FakeDashboardStore) GetDashboardsByLibraryPanelUID(ctx context.Context, libraryPanelUID string, orgID int64) ([]*DashboardRef, error) {
|
||||||
ret := _m.Called(ctx, libraryPanelUID, orgID)
|
ret := _m.Called(ctx, libraryPanelUID, orgID)
|
||||||
|
|
|
@ -4,33 +4,18 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"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"
|
"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"
|
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||||
dashsnapdb "github.com/grafana/grafana/pkg/services/dashboardsnapshots/database"
|
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"
|
"github.com/grafana/grafana/pkg/services/secrets/database"
|
||||||
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
|
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/setting"
|
||||||
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
|
||||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -81,7 +66,6 @@ func TestIntegrationDashboardSnapshotsService(t *testing.T) {
|
||||||
|
|
||||||
require.Equal(t, rawDashboard, decrypted)
|
require.Equal(t, rawDashboard, decrypted)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("get dashboard snapshot should return the dashboard decrypted", func(t *testing.T) {
|
t.Run("get dashboard snapshot should return the dashboard decrypted", func(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
@ -99,69 +83,3 @@ func TestIntegrationDashboardSnapshotsService(t *testing.T) {
|
||||||
require.Equal(t, rawDashboard, decrypted)
|
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
|
query.DashboardID = id
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
version, err := s.getHistoryThroughK8s(ctx, query.OrgID, query.DashboardUID, query.Version)
|
version, err := s.getHistoryThroughK8s(ctx, query.OrgID, query.DashboardUID, query.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -97,14 +96,6 @@ func (s *Service) Get(ctx context.Context, query *dashver.GetDashboardVersionQue
|
||||||
return version, nil
|
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 {
|
func (s *Service) DeleteExpired(ctx context.Context, cmd *dashver.DeleteExpiredVersionsCommand) error {
|
||||||
versionsToKeep := s.cfg.DashboardVersionsToKeep
|
versionsToKeep := s.cfg.DashboardVersionsToKeep
|
||||||
if versionsToKeep < 1 {
|
if versionsToKeep < 1 {
|
||||||
|
@ -160,7 +151,6 @@ func (s *Service) List(ctx context.Context, query *dashver.ListDashboardVersions
|
||||||
query.Limit = 1000
|
query.Limit = 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
versions, err := s.listHistoryThroughK8s(
|
versions, err := s.listHistoryThroughK8s(
|
||||||
ctx,
|
ctx,
|
||||||
query.OrgID,
|
query.OrgID,
|
||||||
|
@ -174,19 +164,6 @@ func (s *Service) List(ctx context.Context, query *dashver.ListDashboardVersions
|
||||||
return versions, nil
|
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
|
// getDashUIDMaybeEmpty is a helper function which takes a dashboardID and
|
||||||
// returns the UID. If the dashboard is not found, it will return an empty
|
// returns the UID. If the dashboard is not found, it will return an empty
|
||||||
// string.
|
// string.
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"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/apiserver/client"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
|
@ -25,29 +24,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDashboardVersionService(t *testing.T) {
|
func TestDashboardVersionService(t *testing.T) {
|
||||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
t.Run("Get dashboard versions", func(t *testing.T) {
|
||||||
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) {
|
|
||||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||||
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
||||||
mockCli := new(client.MockK8sHandler)
|
mockCli := new(client.MockK8sHandler)
|
||||||
dashboardVersionService.k8sclient = mockCli
|
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)
|
dashboardService.On("GetDashboardUIDByID", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).Return(&dashboards.DashboardRef{UID: "uid"}, nil)
|
||||||
|
|
||||||
creationTimestamp := time.Now().Add(time.Hour * -24).UTC()
|
creationTimestamp := time.Now().Add(time.Hour * -24).UTC()
|
||||||
|
@ -133,7 +115,7 @@ func TestDashboardVersionService(t *testing.T) {
|
||||||
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
||||||
mockCli := new(client.MockK8sHandler)
|
mockCli := new(client.MockK8sHandler)
|
||||||
dashboardVersionService.k8sclient = mockCli
|
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)
|
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"))
|
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) {
|
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) {
|
t.Run("List all versions for a given Dashboard ID through k8s", func(t *testing.T) {
|
||||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||||
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
||||||
mockCli := new(client.MockK8sHandler)
|
mockCli := new(client.MockK8sHandler)
|
||||||
dashboardVersionService.k8sclient = mockCli
|
dashboardVersionService.k8sclient = mockCli
|
||||||
dashboardVersionService.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders)
|
dashboardVersionService.features = featuremgmt.WithFeatures()
|
||||||
|
|
||||||
dashboardService.On("GetDashboardUIDByID", mock.Anything,
|
dashboardService.On("GetDashboardUIDByID", mock.Anything,
|
||||||
mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).
|
mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).
|
||||||
|
@ -301,7 +202,7 @@ func TestListDashboardVersions(t *testing.T) {
|
||||||
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
dashboardVersionService := Service{dashSvc: dashboardService, features: featuremgmt.WithFeatures()}
|
||||||
mockCli := new(client.MockK8sHandler)
|
mockCli := new(client.MockK8sHandler)
|
||||||
dashboardVersionService.k8sclient = mockCli
|
dashboardVersionService.k8sclient = mockCli
|
||||||
dashboardVersionService.features = featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders)
|
dashboardVersionService.features = featuremgmt.WithFeatures()
|
||||||
dashboardService.On("GetDashboardUIDByID", mock.Anything,
|
dashboardService.On("GetDashboardUIDByID", mock.Anything,
|
||||||
mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).
|
mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).
|
||||||
Return(&dashboards.DashboardRef{UID: "uid"}, nil)
|
Return(&dashboards.DashboardRef{UID: "uid"}, nil)
|
||||||
|
|
|
@ -483,13 +483,6 @@ var (
|
||||||
Owner: grafanaAppPlatformSquad,
|
Owner: grafanaAppPlatformSquad,
|
||||||
FrontendOnly: true,
|
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",
|
Name: "dashboardDisableSchemaValidationV1",
|
||||||
Description: "Disable schema validation for dashboards/v1",
|
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
|
alertingJiraIntegration,2025-02-14T12:22:04Z,,af8cab92109ecfbb54625ff25a3b90a4f92dc46a,Sonia Aguilar
|
||||||
alertingRuleVersionHistoryRestore,2025-02-17T12:25:32Z,,2014d27defe5668ba07913cec6f2186c90eaaab2,Sonia Aguilar
|
alertingRuleVersionHistoryRestore,2025-02-17T12:25:32Z,,2014d27defe5668ba07913cec6f2186c90eaaab2,Sonia Aguilar
|
||||||
newShareReportDrawer,2025-02-17T19:05:46Z,,9df6412e92559f2329c2baac0025f56fc57c5bf9,Ezequiel Victorero
|
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
|
managedDualWriter,2025-02-19T14:50:39Z,,5a40c84568485da55ccb42998c6d291d310abd56,Ryan McKinley
|
||||||
rendererDisableAppPluginsPreload,2025-02-24T14:43:06Z,,608d974585c696253ac629f3c7bfc3a0043cbd49,Agnès Toulet
|
rendererDisableAppPluginsPreload,2025-02-24T14:43:06Z,,608d974585c696253ac629f3c7bfc3a0043cbd49,Agnès Toulet
|
||||||
assetSriChecks,2025-03-04T10:56:35Z,,bbfeb8d220cc67c329aa2b5d6ed693ae1cb54325,Jack Westbrook
|
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
|
kubernetesLibraryPanels,experimental,@grafana/grafana-app-platform-squad,false,true,false
|
||||||
kubernetesLibraryPanelConnections,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
|
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
|
dashboardDisableSchemaValidationV1,experimental,@grafana/grafana-app-platform-squad,false,false,false
|
||||||
dashboardDisableSchemaValidationV2,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
|
dashboardSchemaValidationLogging,experimental,@grafana/grafana-app-platform-squad,false,false,false
|
||||||
|
|
|
|
@ -259,10 +259,6 @@ const (
|
||||||
// Use the kubernetes API in the frontend for dashboards
|
// Use the kubernetes API in the frontend for dashboards
|
||||||
FlagKubernetesDashboards = "kubernetesDashboards"
|
FlagKubernetesDashboards = "kubernetesDashboards"
|
||||||
|
|
||||||
// FlagKubernetesClientDashboardsFolders
|
|
||||||
// Route the folder and dashboard service requests to k8s
|
|
||||||
FlagKubernetesClientDashboardsFolders = "kubernetesClientDashboardsFolders"
|
|
||||||
|
|
||||||
// FlagDashboardDisableSchemaValidationV1
|
// FlagDashboardDisableSchemaValidationV1
|
||||||
// Disable schema validation for dashboards/v1
|
// Disable schema validation for dashboards/v1
|
||||||
FlagDashboardDisableSchemaValidationV1 = "dashboardDisableSchemaValidationV1"
|
FlagDashboardDisableSchemaValidationV1 = "dashboardDisableSchemaValidationV1"
|
||||||
|
|
|
@ -1729,19 +1729,6 @@
|
||||||
"hideFromDocs": true
|
"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": {
|
"metadata": {
|
||||||
"name": "kubernetesDashboards",
|
"name": "kubernetesDashboards",
|
||||||
|
|
|
@ -112,7 +112,6 @@ func ProvideService(
|
||||||
ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(folderStore, srv))
|
ac.RegisterScopeAttributeResolver(dashboards.NewFolderIDScopeResolver(folderStore, srv))
|
||||||
ac.RegisterScopeAttributeResolver(dashboards.NewFolderUIDScopeResolver(srv))
|
ac.RegisterScopeAttributeResolver(dashboards.NewFolderUIDScopeResolver(srv))
|
||||||
|
|
||||||
if features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
k8sHandler := client.NewK8sHandler(
|
k8sHandler := client.NewK8sHandler(
|
||||||
dual,
|
dual,
|
||||||
request.GetNamespaceMapper(cfg),
|
request.GetNamespaceMapper(cfg),
|
||||||
|
@ -129,9 +128,7 @@ func ProvideService(
|
||||||
|
|
||||||
srv.unifiedStore = unifiedStore
|
srv.unifiedStore = unifiedStore
|
||||||
srv.k8sclient = k8sHandler
|
srv.k8sclient = k8sHandler
|
||||||
}
|
|
||||||
|
|
||||||
if features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
dashHandler := client.NewK8sHandler(
|
dashHandler := client.NewK8sHandler(
|
||||||
dual,
|
dual,
|
||||||
request.GetNamespaceMapper(cfg),
|
request.GetNamespaceMapper(cfg),
|
||||||
|
@ -144,7 +141,6 @@ func ProvideService(
|
||||||
features,
|
features,
|
||||||
)
|
)
|
||||||
srv.dashboardK8sClient = dashHandler
|
srv.dashboardK8sClient = dashHandler
|
||||||
}
|
|
||||||
|
|
||||||
return srv
|
return srv
|
||||||
}
|
}
|
||||||
|
@ -201,33 +197,23 @@ func (s *Service) DBMigration(db db.DB) {
|
||||||
func (s *Service) CountFoldersInOrg(ctx context.Context, orgID int64) (int64, error) {
|
func (s *Service) CountFoldersInOrg(ctx context.Context, orgID int64) (int64, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.CountFoldersInOrg")
|
ctx, span := s.tracer.Start(ctx, "folder.CountFoldersInOrg")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return s.unifiedStore.CountInOrg(ctx, orgID)
|
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) {
|
func (s *Service) SearchFolders(ctx context.Context, q folder.SearchFoldersQuery) (model.HitList, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.SearchFolders")
|
ctx, span := s.tracer.Start(ctx, "folder.SearchFolders")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - implement filtering by alerting folders and k6 folders (see the dashboards store `FindDashboards` method for reference)
|
// - 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)
|
// - 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 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) {
|
func (s *Service) GetFolders(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.GetFolders")
|
ctx, span := s.tracer.Start(ctx, "folder.GetFolders")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return s.getFoldersFromApiServer(ctx, q)
|
return s.getFoldersFromApiServer(ctx, q)
|
||||||
}
|
}
|
||||||
return s.GetFoldersLegacy(ctx, q)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetFoldersLegacy(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) {
|
func (s *Service) GetFoldersLegacy(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.GetFoldersLegacy")
|
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) {
|
func (s *Service) Get(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.Get")
|
ctx, span := s.tracer.Start(ctx, "folder.Get")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return s.getFromApiServer(ctx, q)
|
return s.getFromApiServer(ctx, q)
|
||||||
}
|
}
|
||||||
return s.GetLegacy(ctx, q)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) {
|
func (s *Service) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.GetLegacy")
|
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) {
|
func (s *Service) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.GetChildren")
|
ctx, span := s.tracer.Start(ctx, "folder.GetChildren")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return s.getChildrenFromApiServer(ctx, q)
|
return s.getChildrenFromApiServer(ctx, q)
|
||||||
}
|
}
|
||||||
return s.GetChildrenLegacy(ctx, q)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
func (s *Service) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.GetChildrenLegacy")
|
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) {
|
func (s *Service) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.GetParents")
|
ctx, span := s.tracer.Start(ctx, "folder.GetParents")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return s.getParentsFromApiServer(ctx, q)
|
return s.getParentsFromApiServer(ctx, q)
|
||||||
}
|
}
|
||||||
return s.GetParentsLegacy(ctx, q)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetParentsLegacy(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
|
func (s *Service) GetParentsLegacy(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.GetParentsLegacy")
|
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) {
|
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.createOnApiServer(ctx, cmd)
|
||||||
}
|
}
|
||||||
return s.CreateLegacy(ctx, cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) CreateLegacy(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
|
func (s *Service) CreateLegacy(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
|
||||||
if cmd.SignedInUser == nil || cmd.SignedInUser.IsNil() {
|
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) {
|
func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.Update")
|
ctx, span := s.tracer.Start(ctx, "folder.Update")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return s.updateOnApiServer(ctx, cmd)
|
return s.updateOnApiServer(ctx, cmd)
|
||||||
}
|
}
|
||||||
return s.UpdateLegacy(ctx, cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) UpdateLegacy(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) {
|
func (s *Service) UpdateLegacy(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.UpdateLegacy")
|
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 {
|
func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) error {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.Delete")
|
ctx, span := s.tracer.Start(ctx, "folder.Delete")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return s.deleteFromApiServer(ctx, cmd)
|
return s.deleteFromApiServer(ctx, cmd)
|
||||||
}
|
}
|
||||||
return s.DeleteLegacy(ctx, cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) DeleteLegacy(ctx context.Context, cmd *folder.DeleteFolderCommand) error {
|
func (s *Service) DeleteLegacy(ctx context.Context, cmd *folder.DeleteFolderCommand) error {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.DeleteLegacy")
|
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) {
|
func (s *Service) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.Move")
|
ctx, span := s.tracer.Start(ctx, "folder.Move")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return s.moveOnApiServer(ctx, cmd)
|
return s.moveOnApiServer(ctx, cmd)
|
||||||
}
|
}
|
||||||
return s.MoveLegacy(ctx, cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) MoveLegacy(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) {
|
func (s *Service) MoveLegacy(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.MoveLegacy")
|
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) {
|
func (s *Service) GetDescendantCounts(ctx context.Context, q *folder.GetDescendantCountsQuery) (folder.DescendantCounts, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.GetDescendantCounts")
|
ctx, span := s.tracer.Start(ctx, "folder.GetDescendantCounts")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
return s.getDescendantCountsFromApiServer(ctx, q)
|
return s.getDescendantCountsFromApiServer(ctx, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.GetDescendantCountsLegacy(ctx, q)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetDescendantCountsLegacy(ctx context.Context, q *folder.GetDescendantCountsQuery) (folder.DescendantCounts, error) {
|
func (s *Service) GetDescendantCountsLegacy(ctx context.Context, q *folder.GetDescendantCountsQuery) (folder.DescendantCounts, error) {
|
||||||
ctx, span := s.tracer.Start(ctx, "folder.GetDescendantCountsLegacy")
|
ctx, span := s.tracer.Start(ctx, "folder.GetDescendantCountsLegacy")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
|
@ -193,9 +193,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
|
||||||
ExpectedUser: &user.User{},
|
ExpectedUser: &user.User{},
|
||||||
}
|
}
|
||||||
|
|
||||||
featuresArr := []any{
|
features := featuremgmt.WithFeatures()
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders}
|
|
||||||
features := featuremgmt.WithFeatures(featuresArr...)
|
|
||||||
|
|
||||||
tracer := noop.NewTracerProvider().Tracer("TestIntegrationFolderServiceViaUnifiedStorage")
|
tracer := noop.NewTracerProvider().Tracer("TestIntegrationFolderServiceViaUnifiedStorage")
|
||||||
dashboardStore := dashboards.NewFakeDashboardStore(t)
|
dashboardStore := dashboards.NewFakeDashboardStore(t)
|
||||||
|
@ -521,7 +519,7 @@ func TestSearchFoldersFromApiServer(t *testing.T) {
|
||||||
tracer := noop.NewTracerProvider().Tracer("TestSearchFoldersFromApiServer")
|
tracer := noop.NewTracerProvider().Tracer("TestSearchFoldersFromApiServer")
|
||||||
service := Service{
|
service := Service{
|
||||||
k8sclient: fakeK8sClient,
|
k8sclient: fakeK8sClient,
|
||||||
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
|
features: featuremgmt.WithFeatures(),
|
||||||
unifiedStore: folderStore,
|
unifiedStore: folderStore,
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||||
|
@ -764,7 +762,7 @@ func TestGetFoldersFromApiServer(t *testing.T) {
|
||||||
tracer := noop.NewTracerProvider().Tracer("TestGetFoldersFromApiServer")
|
tracer := noop.NewTracerProvider().Tracer("TestGetFoldersFromApiServer")
|
||||||
service := Service{
|
service := Service{
|
||||||
k8sclient: fakeK8sClient,
|
k8sclient: fakeK8sClient,
|
||||||
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
|
features: featuremgmt.WithFeatures(),
|
||||||
unifiedStore: folderStore,
|
unifiedStore: folderStore,
|
||||||
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
|
@ -866,7 +864,7 @@ func TestIntegrationDeleteFoldersFromApiServer(t *testing.T) {
|
||||||
publicDashboardService: publicDashboardFakeService,
|
publicDashboardService: publicDashboardFakeService,
|
||||||
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
|
||||||
registry: make(map[string]folder.RegistryService),
|
registry: make(map[string]folder.RegistryService),
|
||||||
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
|
features: featuremgmt.WithFeatures(),
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
}
|
}
|
||||||
user := &user.SignedInUser{OrgID: 1}
|
user := &user.SignedInUser{OrgID: 1}
|
||||||
|
|
|
@ -15,16 +15,49 @@ type FakeService struct {
|
||||||
ExpectedError error
|
ExpectedError error
|
||||||
ExpectedDescendantCounts map[string]int64
|
ExpectedDescendantCounts map[string]int64
|
||||||
LastQuery folder.GetFoldersQuery
|
LastQuery folder.GetFoldersQuery
|
||||||
|
foldersByUID map[string]*folder.Folder
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFakeService() *FakeService {
|
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)
|
var _ folder.Service = (*FakeService)(nil)
|
||||||
|
|
||||||
func (s *FakeService) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
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) {
|
func (s *FakeService) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.FolderReference, error) {
|
||||||
return s.ExpectedFoldersRef, s.ExpectedError
|
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) {
|
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
|
return s.ExpectedFolder, s.ExpectedError
|
||||||
}
|
}
|
||||||
func (s *FakeService) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) {
|
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) {
|
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) {
|
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
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.features.IsEnabled(c, featuremgmt.FlagKubernetesClientDashboardsFolders) {
|
|
||||||
searchQuery := folder.SearchFoldersQuery{
|
searchQuery := folder.SearchFoldersQuery{
|
||||||
OrgID: signedInUser.GetOrgID(),
|
OrgID: signedInUser.GetOrgID(),
|
||||||
Title: query.SearchString,
|
Title: query.SearchString,
|
||||||
|
@ -911,21 +910,3 @@ func getFoldersWithMatchingTitles(c context.Context, l *LibraryElementService, s
|
||||||
}
|
}
|
||||||
return foldersWithMatchingTitles, nil
|
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,
|
Version: 1,
|
||||||
Meta: model.LibraryElementDTOMeta{
|
Meta: model.LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: sc.folder.Title,
|
||||||
FolderUID: "uid_for_ScenarioFolder",
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: sc.initialResult.Result.Meta.Created,
|
Created: sc.initialResult.Result.Meta.Created,
|
||||||
Updated: sc.initialResult.Result.Meta.Updated,
|
Updated: sc.initialResult.Result.Meta.Updated,
|
||||||
|
@ -97,8 +97,8 @@ func TestIntegration_CreateLibraryElement(t *testing.T) {
|
||||||
},
|
},
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: model.LibraryElementDTOMeta{
|
Meta: model.LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: sc.folder.Title,
|
||||||
FolderUID: "uid_for_ScenarioFolder",
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Meta.Created,
|
Created: result.Result.Meta.Created,
|
||||||
Updated: result.Result.Meta.Updated,
|
Updated: result.Result.Meta.Updated,
|
||||||
|
@ -176,8 +176,8 @@ func TestIntegration_CreateLibraryElement(t *testing.T) {
|
||||||
},
|
},
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: model.LibraryElementDTOMeta{
|
Meta: model.LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: sc.folder.Title,
|
||||||
FolderUID: "uid_for_ScenarioFolder",
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Meta.Created,
|
Created: result.Result.Meta.Created,
|
||||||
Updated: result.Result.Meta.Updated,
|
Updated: result.Result.Meta.Updated,
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"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/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"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",
|
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) {
|
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})
|
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||||
resp := sc.service.deleteHandler(sc.reqContext)
|
resp := sc.service.deleteHandler(sc.reqContext)
|
||||||
require.Equal(t, 200, resp.Status())
|
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",
|
scenarioWithPanel(t, "When an admin tries to delete a library panel that is connected, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
dashJSON := map[string]any{
|
sc.dashboardSvc.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{
|
||||||
"panels": []any{
|
{
|
||||||
map[string]any{
|
ID: 1,
|
||||||
"id": int64(1),
|
UID: "test",
|
||||||
"gridPos": map[string]any{
|
|
||||||
"h": 6,
|
|
||||||
"w": 6,
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
},
|
},
|
||||||
},
|
}, nil)
|
||||||
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),
|
|
||||||
}
|
|
||||||
// nolint:staticcheck
|
// 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}, 1)
|
||||||
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
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",
|
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) {
|
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)
|
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, 9999999)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,10 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
"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/libraryelements/model"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
|
searchmodel "github.com/grafana/grafana/pkg/services/search/model"
|
||||||
"github.com/grafana/grafana/pkg/services/search/sort"
|
"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",
|
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) {
|
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
|
// nolint:staticcheck
|
||||||
command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel2")
|
command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel2")
|
||||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||||
|
@ -448,7 +457,7 @@ func TestIntegration_GetAllLibraryElements(t *testing.T) {
|
||||||
},
|
},
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: model.LibraryElementDTOMeta{
|
Meta: model.LibraryElementDTOMeta{
|
||||||
FolderName: "NewFolder",
|
FolderName: newFolder.Title,
|
||||||
FolderUID: newFolder.UID,
|
FolderUID: newFolder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: result.Result.Elements[0].Meta.Created,
|
Created: result.Result.Elements[0].Meta.Created,
|
||||||
|
@ -1171,6 +1180,14 @@ func TestIntegration_GetAllLibraryElements(t *testing.T) {
|
||||||
// Folder name search integration tests
|
// Folder name search integration tests
|
||||||
scenarioWithPanel(t, "When searching by folder name, it should return panels in that folder",
|
scenarioWithPanel(t, "When searching by folder name, it should return panels in that folder",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
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
|
// Create a panel in the existing folder
|
||||||
// nolint:staticcheck
|
// nolint:staticcheck
|
||||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Panel in ScenarioFolder")
|
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",
|
scenarioWithPanel(t, "When searching by partial folder name, it should return panels in matching folders",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
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
|
// Create a panel in the existing folder
|
||||||
// nolint:staticcheck
|
// nolint:staticcheck
|
||||||
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Test Panel")
|
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Test Panel")
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
@ -57,7 +56,7 @@ func TestIntegration_GetLibraryElement(t *testing.T) {
|
||||||
},
|
},
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: model.LibraryElementDTOMeta{
|
Meta: model.LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: sc.folder.Title,
|
||||||
FolderUID: sc.folder.UID,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: res.Result.Meta.Created,
|
Created: res.Result.Meta.Created,
|
||||||
|
@ -120,6 +119,8 @@ func TestIntegration_GetLibraryElement(t *testing.T) {
|
||||||
SignedInUser: sc.reqContext.SignedInUser,
|
SignedInUser: sc.reqContext.SignedInUser,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
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 {
|
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)
|
elem, err := sc.service.GetLibraryElement(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, session, result.UID)
|
||||||
require.NoError(t, err)
|
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",
|
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) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
dashJSON := map[string]any{
|
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, 1)
|
||||||
"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)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected := func(res libraryElementResult) libraryElementResult {
|
expected := func(res libraryElementResult) libraryElementResult {
|
||||||
|
@ -187,7 +156,7 @@ func TestIntegration_GetLibraryElement(t *testing.T) {
|
||||||
},
|
},
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: model.LibraryElementDTOMeta{
|
Meta: model.LibraryElementDTOMeta{
|
||||||
FolderName: "ScenarioFolder",
|
FolderName: sc.folder.Title,
|
||||||
FolderUID: sc.folder.UID,
|
FolderUID: sc.folder.UID,
|
||||||
ConnectedDashboards: 1,
|
ConnectedDashboards: 1,
|
||||||
Created: res.Result.Meta.Created,
|
Created: res.Result.Meta.Created,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
"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/libraryelements/model"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"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",
|
scenarioWithPanel(t, "When an admin tries to patch a library panel that exists, it should succeed",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
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{
|
cmd := model.PatchLibraryElementCommand{
|
||||||
FolderID: newFolder.ID, // nolint:staticcheck
|
FolderID: newFolder.ID, // nolint:staticcheck
|
||||||
FolderUID: &newFolder.UID,
|
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",
|
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) {
|
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{
|
cmd := model.PatchLibraryElementCommand{
|
||||||
FolderID: newFolder.ID, // nolint:staticcheck
|
FolderID: newFolder.ID, // nolint:staticcheck
|
||||||
FolderUID: &newFolder.UID,
|
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",
|
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) {
|
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
|
// nolint:staticcheck
|
||||||
command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel")
|
command := getCreatePanelCommand(newFolder.ID, newFolder.UID, "Text - Library Panel")
|
||||||
sc.ctx.Req.Body = mockRequestBody(command)
|
sc.ctx.Req.Body = mockRequestBody(command)
|
||||||
resp := sc.service.createHandler(sc.reqContext)
|
sc.service.createHandler(sc.reqContext)
|
||||||
var result = validateAndUnMarshalResponse(t, resp)
|
|
||||||
cmd := model.PatchLibraryElementCommand{
|
cmd := model.PatchLibraryElementCommand{
|
||||||
FolderID: 1, // nolint:staticcheck
|
FolderID: newFolder.ID, // nolint:staticcheck
|
||||||
FolderUID: &sc.folder.UID,
|
FolderUID: &newFolder.UID,
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Kind: int64(model.PanelElement),
|
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)
|
sc.ctx.Req.Body = mockRequestBody(cmd)
|
||||||
resp = sc.service.patchHandler(sc.reqContext)
|
resp := sc.service.patchHandler(sc.reqContext)
|
||||||
require.Equal(t, 400, resp.Status())
|
require.Equal(t, 400, resp.Status())
|
||||||
})
|
})
|
||||||
|
|
||||||
scenarioWithPanel(t, "When an admin tries to patch a library panel in another org, it should fail",
|
scenarioWithPanel(t, "When an admin tries to patch a library panel in another org, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
|
sc.folderSvc.ExpectedFolder = nil
|
||||||
|
sc.folderSvc.ExpectedError = folder.ErrFolderNotFound
|
||||||
cmd := model.PatchLibraryElementCommand{
|
cmd := model.PatchLibraryElementCommand{
|
||||||
FolderID: sc.folder.ID, // nolint:staticcheck
|
FolderID: sc.folder.ID, // nolint:staticcheck
|
||||||
FolderUID: &sc.folder.UID,
|
FolderUID: &sc.folder.UID,
|
||||||
|
|
|
@ -16,26 +16,17 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"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/tracing"
|
||||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
"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"
|
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"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"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/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"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/folder/foldertest"
|
||||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||||
ngstore "github.com/grafana/grafana/pkg/services/ngalert/store"
|
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/org/orgimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/publicdashboards"
|
"github.com/grafana/grafana/pkg/services/publicdashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
"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/supportbundles/supportbundlestest"
|
||||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
"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/tests/testsuite"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"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",
|
scenarioWithPanel(t, "When an admin tries to delete a folder that contains connected library elements, it should fail",
|
||||||
func(t *testing.T, sc scenarioContext) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
dashJSON := map[string]any{
|
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, 1)
|
||||||
"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)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = sc.service.DeleteLibraryElementsInFolder(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, sc.folder.UID)
|
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",
|
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) {
|
func(t *testing.T, sc scenarioContext) {
|
||||||
dashJSON := map[string]any{
|
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, 1)
|
||||||
"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)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// add a connection where the dashboard doesn't exist. Shouldn't be returned in the list
|
// 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,
|
ID: sc.initialResult.Result.ID,
|
||||||
Kind: sc.initialResult.Result.Kind,
|
Kind: sc.initialResult.Result.Kind,
|
||||||
ElementID: 1,
|
ElementID: 1,
|
||||||
ConnectionID: dashInDB.ID,
|
ConnectionID: 1,
|
||||||
ConnectionUID: dashInDB.UID,
|
|
||||||
Created: res.Result[0].Created,
|
Created: res.Result[0].Created,
|
||||||
CreatedBy: librarypanel.LibraryElementDTOMetaUser{
|
CreatedBy: librarypanel.LibraryElementDTOMetaUser{
|
||||||
Id: 1,
|
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})
|
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||||
resp := sc.service.getConnectionsHandler(sc.reqContext)
|
resp := sc.service.getConnectionsHandler(sc.reqContext)
|
||||||
var result = validateAndUnMarshalConnectionResponse(t, resp)
|
var result = validateAndUnMarshalConnectionResponse(t, resp)
|
||||||
|
@ -239,13 +168,7 @@ func TestIntegration_GetLibraryPanelConnections(t *testing.T) {
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
dash := dashboards.Dashboard{
|
err = sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{sc.initialResult.Result.UID}, 1)
|
||||||
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)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -319,77 +242,26 @@ type scenarioContext struct {
|
||||||
initialResult libraryElementResult
|
initialResult libraryElementResult
|
||||||
sqlStore db.DB
|
sqlStore db.DB
|
||||||
log log.Logger
|
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 {
|
func createFolder(t *testing.T, sc scenarioContext, title string, folderSvc *foldertest.FakeService) *folder.Folder {
|
||||||
// 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 {
|
|
||||||
t.Helper()
|
t.Helper()
|
||||||
ctx := identity.WithRequester(context.Background(), &sc.user)
|
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,
|
OrgID: sc.user.OrgID, Title: title, UID: "uid_for_" + title, SignedInUser: &sc.user,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
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
|
// 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.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(folder.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(folder.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 {
|
func validateAndUnMarshalResponse(t *testing.T, resp response.Response) libraryElementResult {
|
||||||
|
@ -465,32 +337,28 @@ func setupTestScenario(t *testing.T) scenarioContext {
|
||||||
sqlStore, cfg := db.InitTestDBWithCfg(t)
|
sqlStore, cfg := db.InitTestDBWithCfg(t)
|
||||||
t.Cleanup(db.CleanupTestDB)
|
t.Cleanup(db.CleanupTestDB)
|
||||||
quotaService := quotatest.New(false, nil)
|
quotaService := quotatest.New(false, nil)
|
||||||
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore))
|
|
||||||
require.NoError(t, err)
|
|
||||||
ac := acimpl.ProvideAccessControl(features)
|
ac := acimpl.ProvideAccessControl(features)
|
||||||
folderPermissions := acmock.NewMockedPermissionsService()
|
folderPermissions := acmock.NewMockedPermissionsService()
|
||||||
folderPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
|
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 := &publicdashboards.FakePublicDashboardServiceWrapper{}
|
||||||
publicDash.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
publicDash.On("DeleteByDashboardUIDs", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||||
folderSvc := folderimpl.ProvideService(
|
|
||||||
fStore, ac, bus.ProvideBus(tracing.InitializeTracerForTest()), dashboardStore, folderStore,
|
folderSvc := foldertest.NewFakeService()
|
||||||
nil, sqlStore, features, supportbundlestest.NewFakeBundleService(), publicDash, cfg, nil, tracing.InitializeTracerForTest(), nil, dualwrite.ProvideTestService(), sort.ProvideService(), apiserver.WithoutRestConfig)
|
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()))
|
alertStore, err := ngstore.ProvideDBStore(cfg, features, sqlStore, &foldertest.FakeService{}, &dashboards.FakeDashboardService{}, ac, bus.ProvideBus(tracing.InitializeTracerForTest()))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = folderSvc.RegisterService(alertStore)
|
err = folderSvc.RegisterService(alertStore)
|
||||||
require.NoError(t, err)
|
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{
|
service := LibraryElementService{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
features: featuremgmt.WithFeatures(),
|
features: featuremgmt.WithFeatures(),
|
||||||
|
@ -531,6 +399,7 @@ func setupTestScenario(t *testing.T) scenarioContext {
|
||||||
SignedInUser: &usr,
|
SignedInUser: &usr,
|
||||||
},
|
},
|
||||||
folderSvc: folderSvc,
|
folderSvc: folderSvc,
|
||||||
|
dashboardSvc: dashService,
|
||||||
}
|
}
|
||||||
|
|
||||||
sc.folder = createFolder(t, sc, "ScenarioFolder", folderSvc)
|
sc.folder = createFolder(t, sc, "ScenarioFolder", folderSvc)
|
||||||
|
|
|
@ -7,43 +7,27 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
"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/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"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/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
"github.com/grafana/grafana/pkg/kinds/librarypanel"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
"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"
|
||||||
"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/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"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/folder/foldertest"
|
||||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||||
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
"github.com/grafana/grafana/pkg/services/libraryelements/model"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
"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/supportbundles/supportbundlestest"
|
||||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
"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/tests/testsuite"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -93,7 +77,7 @@ func TestIntegrationConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||||
Data: simplejson.NewFromAny(dashJSON),
|
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)
|
err := sc.service.ConnectLibraryPanelsForDashboard(sc.ctx, sc.user, dashInDB)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -191,7 +175,7 @@ func TestIntegrationConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||||
Data: simplejson.NewFromAny(dashJSON),
|
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)
|
err = sc.service.ConnectLibraryPanelsForDashboard(sc.ctx, sc.user, dashInDB)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -237,7 +221,7 @@ func TestIntegrationConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||||
Data: simplejson.NewFromAny(dashJSON),
|
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)
|
err := sc.service.ConnectLibraryPanelsForDashboard(sc.ctx, sc.user, dashInDB)
|
||||||
require.EqualError(t, err, errLibraryPanelHeaderUIDMissing.Error())
|
require.EqualError(t, err, errLibraryPanelHeaderUIDMissing.Error())
|
||||||
|
@ -293,7 +277,7 @@ func TestIntegrationConnectLibraryPanelsForDashboard(t *testing.T) {
|
||||||
Title: "Testing ConnectLibraryPanelsForDashboard",
|
Title: "Testing ConnectLibraryPanelsForDashboard",
|
||||||
Data: simplejson.NewFromAny(dashJSON),
|
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)
|
err = sc.elementService.ConnectElementsToDashboard(sc.ctx, sc.user, []string{sc.initialResult.Result.UID}, dashInDB.ID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -411,7 +395,7 @@ func TestIntegrationImportLibraryPanelsForDashboard(t *testing.T) {
|
||||||
element, err := sc.elementService.GetElement(sc.ctx, sc.user,
|
element, err := sc.elementService.GetElement(sc.ctx, sc.user,
|
||||||
model.GetLibraryElementCommand{UID: missingUID, FolderName: dashboards.RootFolderName})
|
model.GetLibraryElementCommand{UID: missingUID, FolderName: dashboards.RootFolderName})
|
||||||
require.NoError(t, err)
|
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)
|
var result = toLibraryElement(t, element)
|
||||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||||
t.Fatalf("Result mismatch (-want +got):\n%s", 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,
|
element, err := sc.elementService.GetElement(sc.ctx, sc.user,
|
||||||
model.GetLibraryElementCommand{UID: existingUID, FolderName: dashboards.RootFolderName})
|
model.GetLibraryElementCommand{UID: existingUID, FolderName: dashboards.RootFolderName})
|
||||||
require.NoError(t, err)
|
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.FolderUID = sc.initialResult.Result.FolderUID
|
||||||
expected.Description = sc.initialResult.Result.Description
|
expected.Description = sc.initialResult.Result.Description
|
||||||
expected.Meta.FolderUID = sc.folder.UID
|
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})
|
element, err := sc.elementService.GetElement(sc.ctx, sc.user, model.GetLibraryElementCommand{UID: outsideUID, FolderName: dashboards.RootFolderName})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := getExpected(t, element, outsideUID, outsideName, outsideModel)
|
expected := getExpected(t, element, outsideUID, outsideName, outsideModel, "Test Folder")
|
||||||
result := toLibraryElement(t, element)
|
result := toLibraryElement(t, element)
|
||||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||||
t.Fatalf("Result mismatch (-want +got):\n%s", 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})
|
element, err = sc.elementService.GetElement(sc.ctx, sc.user, model.GetLibraryElementCommand{UID: insideUID, FolderName: dashboards.RootFolderName})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected = getExpected(t, element, insideUID, insideName, insideModel)
|
expected = getExpected(t, element, insideUID, insideName, insideModel, "Test Folder")
|
||||||
result = toLibraryElement(t, element)
|
result = toLibraryElement(t, element)
|
||||||
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
if diff := cmp.Diff(expected, result, getCompareOptions()...); diff != "" {
|
||||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||||
|
@ -648,6 +632,8 @@ type scenarioContext struct {
|
||||||
initialResult libraryPanelResult
|
initialResult libraryPanelResult
|
||||||
sqlStore db.DB
|
sqlStore db.DB
|
||||||
lps LibraryPanelService
|
lps LibraryPanelService
|
||||||
|
mockDashboard *dashboards.FakeDashboardService
|
||||||
|
mockFolder *foldertest.FakeService
|
||||||
}
|
}
|
||||||
|
|
||||||
func toLibraryElement(t *testing.T, res model.LibraryElementDTO) libraryElement {
|
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)
|
marshalled, err := json.Marshal(lEModel)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
var libModel libraryElementModel
|
var libModel libraryElementModel
|
||||||
|
@ -704,7 +690,7 @@ func getExpected(t *testing.T, res model.LibraryElementDTO, UID string, name str
|
||||||
Model: libModel,
|
Model: libModel,
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Meta: model.LibraryElementDTOMeta{
|
Meta: model.LibraryElementDTOMeta{
|
||||||
FolderName: "General",
|
FolderName: folderName,
|
||||||
FolderUID: res.FolderUID,
|
FolderUID: res.FolderUID,
|
||||||
ConnectedDashboards: 0,
|
ConnectedDashboards: 0,
|
||||||
Created: res.Meta.Created,
|
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 {
|
return dash
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func scenarioWithLibraryPanel(t *testing.T, desc string, fn func(t *testing.T, sc scenarioContext)) {
|
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)) {
|
func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioContext)) {
|
||||||
t.Helper()
|
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)
|
sqlStore, cfg := db.InitTestDBWithCfg(t)
|
||||||
quotaService := quotatest.New(false, nil)
|
quotaService := quotatest.New(false, nil)
|
||||||
features := featuremgmt.WithFeatures()
|
features := featuremgmt.WithFeatures()
|
||||||
|
|
||||||
ac := actest.FakeAccessControl{ExpectedEvaluate: true}
|
ac := actest.FakeAccessControl{ExpectedEvaluate: true}
|
||||||
dashStore := &dashboards.FakeDashboardStore{}
|
mockDashboardService := dashboards.NewFakeDashboardService(t)
|
||||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
mockFolderService := foldertest.NewFakeService()
|
||||||
dashPermissionService := acmock.NewMockedPermissionsService()
|
mockFolder := &folder.Folder{
|
||||||
folderSvc := foldertest.NewFakeService()
|
ID: 1,
|
||||||
folderSvc.ExpectedFolder = &folder.Folder{ID: 1}
|
UID: "test-folder-uid",
|
||||||
dashService, err := dashboardservice.ProvideDashboardServiceImpl(
|
Title: "Test Folder",
|
||||||
cfg, dashStore, folderStore,
|
URL: "/dashboards/f/test-folder-uid/test-folder",
|
||||||
features, acmock.NewMockedPermissionsService(), ac, actest.FakeService{}, folderSvc,
|
Version: 0,
|
||||||
nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil, dualwrite.ProvideTestService(), sort.ProvideService(),
|
Created: time.Now(),
|
||||||
serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()),
|
Updated: time.Now(),
|
||||||
kvstore.NewFakeKVStore())
|
UpdatedBy: 0,
|
||||||
require.NoError(t, err)
|
CreatedBy: 0,
|
||||||
dashService.RegisterDashboardPermissions(dashPermissionService)
|
HasACL: false,
|
||||||
|
}
|
||||||
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, features, tagimpl.ProvideService(sqlStore))
|
mockFolderService.ExpectedFolder = mockFolder
|
||||||
require.NoError(t, err)
|
elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), mockFolderService, features, ac, mockDashboardService, nil, nil)
|
||||||
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)
|
|
||||||
service := LibraryPanelService{
|
service := LibraryPanelService{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
LibraryElementService: elementService,
|
LibraryElementService: elementService,
|
||||||
FolderService: folderService,
|
FolderService: mockFolderService,
|
||||||
}
|
}
|
||||||
|
|
||||||
usr := &user.SignedInUser{
|
usr := &user.SignedInUser{
|
||||||
UserID: 1,
|
UserID: 1,
|
||||||
Name: "Signed In User",
|
Name: "Signed In User",
|
||||||
|
@ -872,17 +802,12 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
OrgRole: role,
|
OrgRole: role,
|
||||||
LastSeenAt: time.Now(),
|
LastSeenAt: time.Now(),
|
||||||
// Allow the user to create folders
|
|
||||||
Permissions: map[int64]map[string][]string{
|
Permissions: map[int64]map[string][]string{
|
||||||
orgID: {
|
orgID: {
|
||||||
dashboards.ActionFoldersRead: {dashboards.ScopeFoldersAll},
|
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{
|
cmd := user.CreateUserCommand{
|
||||||
Email: "user.in.db@test.com",
|
Email: "user.in.db@test.com",
|
||||||
Name: "User In DB",
|
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)
|
require.NoError(t, err)
|
||||||
_, err = usrSvc.Create(context.Background(), &cmd)
|
_, err = usrSvc.Create(context.Background(), &cmd)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
sc := scenarioContext{
|
sc := scenarioContext{
|
||||||
user: usr,
|
user: usr,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
@ -905,21 +831,11 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
||||||
elementService: elementService,
|
elementService: elementService,
|
||||||
sqlStore: sqlStore,
|
sqlStore: sqlStore,
|
||||||
lps: service,
|
lps: service,
|
||||||
|
mockDashboard: mockDashboardService,
|
||||||
|
mockFolder: mockFolderService,
|
||||||
}
|
}
|
||||||
|
|
||||||
foldr := createFolder(t, sc, "ScenarioFolder")
|
sc.folder = mockFolder
|
||||||
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,
|
|
||||||
}
|
|
||||||
fn(t, sc)
|
fn(t, sc)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -571,6 +571,7 @@ func TestRouteConvertPrometheusGetRuleGroup(t *testing.T) {
|
||||||
fldr.ParentUID = ""
|
fldr.ParentUID = ""
|
||||||
folderService.ExpectedFolder = fldr
|
folderService.ExpectedFolder = fldr
|
||||||
folderService.ExpectedFolders = []*folder.Folder{fldr}
|
folderService.ExpectedFolders = []*folder.Folder{fldr}
|
||||||
|
folderService.AddFolder(fldr)
|
||||||
ruleStore.Folders[1] = append(ruleStore.Folders[1], fldr)
|
ruleStore.Folders[1] = append(ruleStore.Folders[1], fldr)
|
||||||
|
|
||||||
// Create rules in both folders
|
// Create rules in both folders
|
||||||
|
@ -669,6 +670,8 @@ func TestRouteConvertPrometheusGetNamespace(t *testing.T) {
|
||||||
fldr2 := randFolder()
|
fldr2 := randFolder()
|
||||||
fldr2.ParentUID = ""
|
fldr2.ParentUID = ""
|
||||||
folderService.ExpectedFolders = []*folder.Folder{fldr, fldr2}
|
folderService.ExpectedFolders = []*folder.Folder{fldr, fldr2}
|
||||||
|
folderService.AddFolder(fldr)
|
||||||
|
folderService.AddFolder(fldr2)
|
||||||
ruleStore.Folders[1] = append(ruleStore.Folders[1], fldr, fldr2)
|
ruleStore.Folders[1] = append(ruleStore.Folders[1], fldr, fldr2)
|
||||||
|
|
||||||
// Create a Grafana rule for each Prometheus rule
|
// Create a Grafana rule for each Prometheus rule
|
||||||
|
@ -798,6 +801,7 @@ func TestRouteConvertPrometheusGetRules(t *testing.T) {
|
||||||
// Create a folder in the root
|
// Create a folder in the root
|
||||||
fldr := randFolder()
|
fldr := randFolder()
|
||||||
fldr.ParentUID = ""
|
fldr.ParentUID = ""
|
||||||
|
folderService.AddFolder(fldr)
|
||||||
folderService.ExpectedFolders = []*folder.Folder{fldr}
|
folderService.ExpectedFolders = []*folder.Folder{fldr}
|
||||||
ruleStore.Folders[1] = append(ruleStore.Folders[1], 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/log/logtest"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"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"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"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/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"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/folder/foldertest"
|
||||||
ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol/fakes"
|
"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/provisioning"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
ngalertfakes "github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
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"
|
"github.com/grafana/grafana/pkg/services/secrets"
|
||||||
secrets_fakes "github.com/grafana/grafana/pkg/services/secrets/fakes"
|
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/services/user"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"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/tests/testsuite"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
|
@ -353,14 +345,6 @@ func TestIntegrationProvisioningApi(t *testing.T) {
|
||||||
orgID := int64(2)
|
orgID := int64(2)
|
||||||
|
|
||||||
rule := createTestAlertRule("rule", orgID)
|
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)
|
insertRuleInOrg(t, sut, rule, orgID)
|
||||||
rule.FolderUID = "does-not-exist"
|
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.Req.Header = map[string][]string{"X-Disable-Provenance": {"true"}}
|
||||||
rc.OrgID = 3
|
rc.OrgID = 3
|
||||||
rule := createTestAlertRule("rule", 1)
|
rule := createTestAlertRule("rule", 1)
|
||||||
|
rule.FolderUID = "folder-uid3"
|
||||||
_, 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)
|
|
||||||
|
|
||||||
response := sut.RoutePostAlertRule(&rc, rule)
|
response := sut.RoutePostAlertRule(&rc, rule)
|
||||||
|
|
||||||
|
@ -486,13 +463,7 @@ func TestIntegrationProvisioningApi(t *testing.T) {
|
||||||
rule.UID = uid
|
rule.UID = uid
|
||||||
|
|
||||||
orgID := int64(3)
|
orgID := int64(3)
|
||||||
_, err := sut.folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
rule.FolderUID = "folder-uid3"
|
||||||
UID: "folder-uid",
|
|
||||||
Title: "Folder Title",
|
|
||||||
OrgID: orgID,
|
|
||||||
SignedInUser: &user.SignedInUser{OrgID: orgID},
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
insertRuleInOrg(t, sut, rule, orgID)
|
insertRuleInOrg(t, sut, rule, orgID)
|
||||||
rc := createTestRequestCtx()
|
rc := createTestRequestCtx()
|
||||||
|
@ -560,14 +531,7 @@ func TestIntegrationProvisioningApi(t *testing.T) {
|
||||||
uid := util.GenerateShortUID()
|
uid := util.GenerateShortUID()
|
||||||
rule := createTestAlertRule("rule", 3)
|
rule := createTestAlertRule("rule", 3)
|
||||||
rule.UID = uid
|
rule.UID = uid
|
||||||
|
rule.FolderUID = "folder-uid3"
|
||||||
_, 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)
|
|
||||||
|
|
||||||
insertRuleInOrg(t, sut, rule, 3)
|
insertRuleInOrg(t, sut, rule, 3)
|
||||||
|
|
||||||
|
@ -2053,7 +2017,7 @@ func createTestEnv(t *testing.T, testConfig string) testEnvironment {
|
||||||
GetsConfig(models.AlertConfiguration{
|
GetsConfig(models.AlertConfiguration{
|
||||||
AlertmanagerConfiguration: string(raw),
|
AlertmanagerConfiguration: string(raw),
|
||||||
})
|
})
|
||||||
sqlStore, cfg := db.InitTestDBWithCfg(t)
|
sqlStore, _ := db.InitTestDBWithCfg(t)
|
||||||
|
|
||||||
quotas := &provisioning.MockQuotaChecker{}
|
quotas := &provisioning.MockQuotaChecker{}
|
||||||
quotas.EXPECT().LimitOK()
|
quotas.EXPECT().LimitOK()
|
||||||
|
@ -2077,14 +2041,39 @@ func createTestEnv(t *testing.T, testConfig string) testEnvironment {
|
||||||
}}, nil).Maybe()
|
}}, nil).Maybe()
|
||||||
|
|
||||||
ac := &recordingAccessControlFake{}
|
ac := &recordingAccessControlFake{}
|
||||||
dashboardStore, err := database.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore))
|
folderService := foldertest.NewFakeService()
|
||||||
require.NoError(t, err)
|
folder1 := &folder.Folder{
|
||||||
|
UID: "folder-uid",
|
||||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
Title: "Folder Title",
|
||||||
fStore := folderimpl.ProvideStore(sqlStore)
|
Fullpath: "Folder Title",
|
||||||
folderService := folderimpl.ProvideService(
|
OrgID: 1,
|
||||||
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)
|
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{
|
store := store.DBstore{
|
||||||
Logger: log,
|
Logger: log,
|
||||||
SQLStore: sqlStore,
|
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{}
|
ruleAuthz := &fakes.FakeRuleService{}
|
||||||
|
|
||||||
features := featuremgmt.WithFeatures()
|
features := featuremgmt.WithFeatures()
|
||||||
|
|
|
@ -27,12 +27,12 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/testutil"
|
"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/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
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/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
@ -242,10 +242,10 @@ func TestIntegration_GetAlertRulesForScheduling(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
folderService := setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures())
|
fakeFolderService := foldertest.NewFakeService()
|
||||||
b := &fakeBus{}
|
b := &fakeBus{}
|
||||||
logger := &logtest.Fake{}
|
logger := &logtest.Fake{}
|
||||||
store := createTestStore(sqlStore, folderService, logger, cfg.UnifiedAlerting, b)
|
store := createTestStore(sqlStore, fakeFolderService, logger, cfg.UnifiedAlerting, b)
|
||||||
store.FeatureToggles = featuremgmt.WithFeatures()
|
store.FeatureToggles = featuremgmt.WithFeatures()
|
||||||
|
|
||||||
gen := models.RuleGen
|
gen := models.RuleGen
|
||||||
|
@ -258,15 +258,38 @@ func TestIntegration_GetAlertRulesForScheduling(t *testing.T) {
|
||||||
|
|
||||||
parentFolderUid := uuid.NewString()
|
parentFolderUid := uuid.NewString()
|
||||||
parentFolderTitle := "Very Parent Folder"
|
parentFolderTitle := "Very Parent Folder"
|
||||||
createFolder(t, store, parentFolderUid, parentFolderTitle, rule1.OrgID, "")
|
|
||||||
rule1FolderTitle := "folder-" + rule1.Title
|
rule1FolderTitle := "folder-" + rule1.Title
|
||||||
rule2FolderTitle := "folder-" + rule2.Title
|
rule2FolderTitle := "folder-" + rule2.Title
|
||||||
rule3FolderTitle := "folder-" + rule3.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 {
|
tc := []struct {
|
||||||
name string
|
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) {
|
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{
|
query := &models.GetAlertRulesForSchedulingQuery{
|
||||||
PopulateFolders: true,
|
PopulateFolders: true,
|
||||||
}
|
}
|
||||||
|
@ -1597,27 +1627,6 @@ func createRule(t *testing.T, store *DBstore, generator *models.AlertRuleGenerat
|
||||||
return rule
|
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 {
|
func setupFolderService(t *testing.T, sqlStore db.DB, cfg *setting.Cfg, features featuremgmt.FeatureToggles) folder.Service {
|
||||||
tracer := tracing.InitializeTracerForTest()
|
tracer := tracing.InitializeTracerForTest()
|
||||||
inProcBus := bus.ProvideBus(tracer)
|
inProcBus := bus.ProvideBus(tracer)
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
|
@ -28,7 +27,7 @@ func TestIntegration_GetUserVisibleNamespaces(t *testing.T) {
|
||||||
|
|
||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
folderService := setupFolderService(t, sqlStore, cfg, featuremgmt.WithFeatures())
|
folderService := foldertest.NewFakeService()
|
||||||
b := &fakeBus{}
|
b := &fakeBus{}
|
||||||
logger := log.New("test-dbstore")
|
logger := log.New("test-dbstore")
|
||||||
store := createTestStore(sqlStore, folderService, logger, cfg.UnifiedAlerting, b)
|
store := createTestStore(sqlStore, folderService, logger, cfg.UnifiedAlerting, b)
|
||||||
|
@ -40,18 +39,14 @@ func TestIntegration_GetUserVisibleNamespaces(t *testing.T) {
|
||||||
IsGrafanaAdmin: true,
|
IsGrafanaAdmin: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
folders := []struct {
|
folders := []*folder.Folder{
|
||||||
uid string
|
{UID: uuid.NewString(), Title: "folder1", ParentUID: "", OrgID: 1},
|
||||||
title string
|
{UID: uuid.NewString(), Title: "folder2", ParentUID: "", OrgID: 1},
|
||||||
parentUid string
|
{UID: uuid.NewString(), Title: "nested/folder", ParentUID: "", OrgID: 1},
|
||||||
}{
|
|
||||||
{uid: uuid.NewString(), title: "folder1", parentUid: ""},
|
|
||||||
{uid: uuid.NewString(), title: "folder2", parentUid: ""},
|
|
||||||
{uid: uuid.NewString(), title: "nested/folder", parentUid: ""},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range folders {
|
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) {
|
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/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
ngalertfakes "github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
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/org"
|
||||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
"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()
|
tracer := tracing.InitializeTracerForTest()
|
||||||
bus := bus.ProvideBus(tracer)
|
bus := bus.ProvideBus(tracer)
|
||||||
folderStore := folderimpl.ProvideDashboardFolderStore(sqlStore)
|
folderService := foldertest.NewFakeService()
|
||||||
dashboardService, dashboardStore := testutil.SetupDashboardService(tb, sqlStore, folderStore, cfg)
|
dashboardService := dashboards.NewFakeDashboardService(tb)
|
||||||
folderService := testutil.SetupFolderService(tb, cfg, sqlStore, dashboardStore, folderStore, bus, options.featureToggles, ac)
|
|
||||||
ruleStore, err := store.ProvideDBStore(cfg, options.featureToggles, sqlStore, folderService, &dashboards.FakeDashboardService{}, ac, bus)
|
ruleStore, err := store.ProvideDBStore(cfg, options.featureToggles, sqlStore, folderService, &dashboards.FakeDashboardService{}, ac, bus)
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
ng, err := ngalert.ProvideService(
|
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(
|
folderSvc := folderimpl.ProvideService(
|
||||||
fStore, acmock.New(), bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderStore,
|
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)
|
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(),
|
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()),
|
serverlock.ProvideService(sqlStore, tracing.InitializeTracerForTest()),
|
||||||
kvstore.NewFakeKVStore())
|
kvstore.NewFakeKVStore())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -10,13 +10,9 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"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"
|
||||||
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
"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/folder/folderimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/login"
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
"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/tag/tagimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
|
||||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
"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.
|
// dashboard store commands that should be called.
|
||||||
dashStore, err := database.ProvideDashboardStore(db, cfg, features, tagimpl.ProvideService(db))
|
dashStore, err := database.ProvideDashboardStore(db, cfg, features, tagimpl.ProvideService(db))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
fStore := folderimpl.ProvideStore(db)
|
fStore := folderimpl.ProvideStore(db)
|
||||||
folderSvc := folderimpl.ProvideService(
|
// create in both the folder & dashboard tables
|
||||||
fStore, actest.FakeAccessControl{ExpectedEvaluate: true}, bus.ProvideBus(tracing.InitializeTracerForTest()), dashStore, folderimpl.ProvideDashboardFolderStore(db),
|
parent, err := fStore.Create(context.Background(), folder.CreateFolderCommand{
|
||||||
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,
|
|
||||||
Title: "parent",
|
Title: "parent",
|
||||||
SignedInUser: usr,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// create subfolder
|
|
||||||
subfolder, err := folderSvc.Create(context.Background(), &folder.CreateFolderCommand{
|
|
||||||
UID: "subfolder",
|
|
||||||
ParentUID: "parent",
|
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
Title: "subfolder",
|
UID: "parent",
|
||||||
SignedInUser: usr,
|
SignedInUser: usr,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
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
|
// create a root level dashboard
|
||||||
_, err = dashStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{
|
_, err = dashStore.SaveDashboard(context.Background(), dashboards.SaveDashboardCommand{
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
|
|
|
@ -158,11 +158,6 @@ func (ss *sqlStatsService) GetSystemStats(ctx context.Context, query *stats.GetS
|
||||||
if ss.IsUnifiedAlertingEnabled() {
|
if ss.IsUnifiedAlertingEnabled() {
|
||||||
sb.Write(`(SELECT COUNT(DISTINCT (` + dialect.Quote("rule_group") + `)) FROM ` + dialect.Quote("alert_rule") + `) AS rule_groups,`)
|
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))
|
sb.Write(ss.roleCounterSQL(ctx))
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,6 @@ protocol = https
|
||||||
|
|
||||||
[feature_toggles]
|
[feature_toggles]
|
||||||
grafanaAPIServerWithExperimentalAPIs = true
|
grafanaAPIServerWithExperimentalAPIs = true
|
||||||
kubernetesClientDashboardsFolders = true
|
|
||||||
|
|
||||||
[unified_storage.folders.folder.grafana.app]
|
[unified_storage.folders.folder.grafana.app]
|
||||||
dualWriterMode = 4
|
dualWriterMode = 4
|
||||||
|
@ -259,7 +258,6 @@ To enable it, add the following to your `custom.ini` under the `[feature_toggles
|
||||||
[feature_toggles]
|
[feature_toggles]
|
||||||
; Used by the Grafana instance
|
; Used by the Grafana instance
|
||||||
unifiedStorageSearchUI = true
|
unifiedStorageSearchUI = true
|
||||||
kubernetesClientDashboardsFolders = true
|
|
||||||
|
|
||||||
; Used by unified storage
|
; Used by unified storage
|
||||||
unifiedStorageSearch = true
|
unifiedStorageSearch = true
|
||||||
|
@ -369,7 +367,6 @@ signing_keys_url = http://localhost:3011/api/signing-keys/keys
|
||||||
mode = "on-prem"
|
mode = "on-prem"
|
||||||
|
|
||||||
[feature_toggles]
|
[feature_toggles]
|
||||||
kubernetesClientDashboardsFolders = true
|
|
||||||
kubernetesDashboardsAPI = true
|
kubernetesDashboardsAPI = true
|
||||||
kubernetesFolders = true
|
kubernetesFolders = true
|
||||||
unifiedStorage = true
|
unifiedStorage = true
|
||||||
|
@ -418,7 +415,6 @@ http_port = 3011
|
||||||
http_addr = "127.0.0.2"
|
http_addr = "127.0.0.2"
|
||||||
|
|
||||||
[feature_toggles]
|
[feature_toggles]
|
||||||
kubernetesClientDashboardsFolders = true
|
|
||||||
kubernetesDashboardsAPI = true
|
kubernetesDashboardsAPI = true
|
||||||
kubernetesFolders = true
|
kubernetesFolders = true
|
||||||
unifiedStorageSearchUI = true
|
unifiedStorageSearchUI = true
|
||||||
|
|
|
@ -19,7 +19,6 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
"github.com/grafana/grafana/pkg/services/dashboardimport"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
||||||
|
@ -42,7 +41,6 @@ func TestIntegrationDashboardServiceValidation(t *testing.T) {
|
||||||
|
|
||||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||||
|
|
||||||
|
@ -279,7 +277,6 @@ func TestIntegrationDashboardQuota(t *testing.T) {
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableQuota: true,
|
EnableQuota: true,
|
||||||
DashboardOrgQuota: &dashboardQuota,
|
DashboardOrgQuota: &dashboardQuota,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||||
|
@ -338,7 +335,6 @@ func TestIntegrationUpdatingProvisionionedDashboards(t *testing.T) {
|
||||||
// Setup Grafana and its Database
|
// Setup Grafana and its Database
|
||||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
provDashboardsDir := filepath.Join(dir, "conf", "provisioning", "dashboards")
|
provDashboardsDir := filepath.Join(dir, "conf", "provisioning", "dashboards")
|
||||||
|
@ -492,7 +488,6 @@ func TestIntegrationCreate(t *testing.T) {
|
||||||
// Setup Grafana and its Database
|
// Setup Grafana and its Database
|
||||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||||
|
@ -648,7 +643,6 @@ func intPtr(n int) *int {
|
||||||
func TestIntegrationPreserveSchemaVersion(t *testing.T) {
|
func TestIntegrationPreserveSchemaVersion(t *testing.T) {
|
||||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||||
|
@ -739,7 +733,6 @@ func TestIntegrationPreserveSchemaVersion(t *testing.T) {
|
||||||
func TestIntegrationImportDashboardWithLibraryPanels(t *testing.T) {
|
func TestIntegrationImportDashboardWithLibraryPanels(t *testing.T) {
|
||||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||||
|
@ -997,7 +990,6 @@ func TestIntegrationDashboardServicePermissions(t *testing.T) {
|
||||||
|
|
||||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||||
tests.CreateUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
tests.CreateUser(t, env.SQLStore, env.Cfg, user.CreateUserCommand{
|
||||||
|
|
|
@ -46,7 +46,7 @@ func TestIntegrationFolderServiceGetFolder(t *testing.T) {
|
||||||
EnableUnifiedAlerting: true,
|
EnableUnifiedAlerting: true,
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
AppModeProduction: true,
|
AppModeProduction: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders, featuremgmt.FlagKubernetesClientDashboardsFolders},
|
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders},
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, p)
|
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, p)
|
||||||
|
@ -116,7 +116,6 @@ func TestIntegrationUpdateFolder(t *testing.T) {
|
||||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableQuota: true,
|
EnableQuota: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||||
|
@ -159,7 +158,6 @@ func TestIntegrationCreateFolder(t *testing.T) {
|
||||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableQuota: true,
|
EnableQuota: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
grafanaListedAddr, _ := testinfra.StartGrafanaEnv(t, dir, path)
|
||||||
|
@ -208,7 +206,6 @@ func TestIntegrationNestedFoldersOn(t *testing.T) {
|
||||||
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableQuota: true,
|
EnableQuota: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
|
||||||
|
@ -362,7 +359,7 @@ func TestIntegrationSharedWithMe(t *testing.T) {
|
||||||
EnableUnifiedAlerting: true,
|
EnableUnifiedAlerting: true,
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
AppModeProduction: true,
|
AppModeProduction: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders, featuremgmt.FlagKubernetesClientDashboardsFolders},
|
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders},
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
||||||
|
@ -416,7 +413,6 @@ func TestIntegrationBasicRoles(t *testing.T) {
|
||||||
EnableUnifiedAlerting: true,
|
EnableUnifiedAlerting: true,
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
AppModeProduction: true,
|
AppModeProduction: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
||||||
|
@ -554,7 +550,7 @@ func TestIntegrationFineGrainedPermissions(t *testing.T) {
|
||||||
EnableUnifiedAlerting: true,
|
EnableUnifiedAlerting: true,
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
AppModeProduction: true,
|
AppModeProduction: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders, featuremgmt.FlagKubernetesClientDashboardsFolders},
|
EnableFeatureToggles: []string{featuremgmt.FlagNestedFolders},
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
||||||
|
|
|
@ -35,7 +35,6 @@ func TestGetFolders(t *testing.T) {
|
||||||
EnableUnifiedAlerting: true,
|
EnableUnifiedAlerting: true,
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
AppModeProduction: true,
|
AppModeProduction: true,
|
||||||
EnableFeatureToggles: []string{featuremgmt.FlagKubernetesClientDashboardsFolders},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, p)
|
||||||
|
|
|
@ -222,12 +222,7 @@ func TestIntegrationLegacySupport(t *testing.T) {
|
||||||
t.Skip("skipping integration test in short mode")
|
t.Skip("skipping integration test in short mode")
|
||||||
}
|
}
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{})
|
||||||
EnableFeatureToggles: []string{
|
|
||||||
// NOTE: when using this feature toggle, the read is always v0!
|
|
||||||
// featuremgmt.FlagKubernetesClientDashboardsFolders
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
clientV0 := helper.GetResourceClient(apis.ResourceClientArgs{
|
clientV0 := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||||
User: helper.Org1.Admin,
|
User: helper.Org1.Admin,
|
||||||
|
|
|
@ -69,7 +69,6 @@ func TestIntegrationDashboardAPIValidation(t *testing.T) {
|
||||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Enable dashboard feature
|
|
||||||
featuremgmt.FlagUnifiedStorageSearch,
|
featuremgmt.FlagUnifiedStorageSearch,
|
||||||
featuremgmt.FlagKubernetesDashboards, // Enable FE-only dashboard feature flag
|
featuremgmt.FlagKubernetesDashboards, // Enable FE-only dashboard feature flag
|
||||||
},
|
},
|
||||||
|
@ -101,7 +100,6 @@ func TestIntegrationDashboardAPIValidation(t *testing.T) {
|
||||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Enable dashboard feature
|
|
||||||
featuremgmt.FlagUnifiedStorageSearch,
|
featuremgmt.FlagUnifiedStorageSearch,
|
||||||
},
|
},
|
||||||
DisableFeatureToggles: []string{
|
DisableFeatureToggles: []string{
|
||||||
|
@ -138,7 +136,6 @@ func TestIntegrationDashboardAPIAuthorization(t *testing.T) {
|
||||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Enable dashboard feature
|
|
||||||
featuremgmt.FlagUnifiedStorageSearch,
|
featuremgmt.FlagUnifiedStorageSearch,
|
||||||
},
|
},
|
||||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||||
|
@ -189,7 +186,6 @@ func TestIntegrationDashboardAPI(t *testing.T) {
|
||||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Enable dashboard feature
|
|
||||||
featuremgmt.FlagUnifiedStorageSearch,
|
featuremgmt.FlagUnifiedStorageSearch,
|
||||||
featuremgmt.FlagKubernetesDashboards,
|
featuremgmt.FlagKubernetesDashboards,
|
||||||
},
|
},
|
||||||
|
|
|
@ -32,7 +32,6 @@ func TestIntegrationLibraryPanelConnections(t *testing.T) {
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
"unifiedStorageSearch",
|
"unifiedStorageSearch",
|
||||||
"kubernetesClientDashboardsFolders",
|
|
||||||
"kubernetesLibraryPanels",
|
"kubernetesLibraryPanels",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -98,7 +97,6 @@ func TestIntegrationLibraryElementPermissions(t *testing.T) {
|
||||||
DisableAnonymous: true,
|
DisableAnonymous: true,
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
"unifiedStorageSearch",
|
"unifiedStorageSearch",
|
||||||
"kubernetesClientDashboardsFolders",
|
|
||||||
"kubernetesLibraryPanels",
|
"kubernetesLibraryPanels",
|
||||||
"grafanaAPIServerWithExperimentalAPIs", // needed until we move it to v0beta1 at least (currently v0alpha1)
|
"grafanaAPIServerWithExperimentalAPIs", // needed until we move it to v0beta1 at least (currently v0alpha1)
|
||||||
},
|
},
|
||||||
|
@ -303,7 +301,6 @@ func TestIntegrationLibraryPanelConnectionsWithFolderAccess(t *testing.T) {
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
"unifiedStorageSearch",
|
"unifiedStorageSearch",
|
||||||
"kubernetesLibraryPanels",
|
"kubernetesLibraryPanels",
|
||||||
"kubernetesClientDashboardsFolders",
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
ctx := createTestContext(t, helper, helper.Org1, dualWriterMode)
|
ctx := createTestContext(t, helper, helper.Org1, dualWriterMode)
|
||||||
|
|
|
@ -51,9 +51,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
||||||
|
|
||||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||||
AppModeProduction: true,
|
AppModeProduction: true,
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{},
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Check discovery client", func(t *testing.T) {
|
t.Run("Check discovery client", func(t *testing.T) {
|
||||||
|
@ -138,9 +136,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
||||||
DualWriterMode: modeDw,
|
DualWriterMode: modeDw,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{},
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -155,7 +151,6 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
featuremgmt.FlagNestedFolders,
|
featuremgmt.FlagNestedFolders,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
@ -172,7 +167,6 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
featuremgmt.FlagNestedFolders,
|
featuremgmt.FlagNestedFolders,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
@ -189,7 +183,6 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
featuremgmt.FlagNestedFolders,
|
featuremgmt.FlagNestedFolders,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
@ -206,7 +199,6 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
featuremgmt.FlagNestedFolders,
|
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.
|
// We set it to 1 here, so we always get forced pagination based on the response size.
|
||||||
UnifiedStorageMaxPageSizeBytes: 1,
|
UnifiedStorageMaxPageSizeBytes: 1,
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
featuremgmt.FlagNestedFolders,
|
featuremgmt.FlagNestedFolders,
|
||||||
},
|
},
|
||||||
}), mode)
|
}), mode)
|
||||||
|
@ -680,7 +671,6 @@ func TestIntegrationFolderCreatePermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagNestedFolders,
|
featuremgmt.FlagNestedFolders,
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -788,7 +778,6 @@ func TestIntegrationFolderGetPermissions(t *testing.T) {
|
||||||
},
|
},
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagNestedFolders,
|
featuremgmt.FlagNestedFolders,
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -972,7 +961,6 @@ func TestIntegrationFoldersCreateAPIEndpointK8S(t *testing.T) {
|
||||||
},
|
},
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagNestedFolders,
|
featuremgmt.FlagNestedFolders,
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1148,7 +1136,6 @@ func TestIntegrationFoldersGetAPIEndpointK8S(t *testing.T) {
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagNestedFolders,
|
featuremgmt.FlagNestedFolders,
|
||||||
featuremgmt.FlagUnifiedStorageSearch,
|
featuremgmt.FlagUnifiedStorageSearch,
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ func TestIntegrationOpenAPIs(t *testing.T) {
|
||||||
h := NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
h := NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||||
AppModeProduction: true,
|
AppModeProduction: true,
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders, // Will be default on by G12
|
|
||||||
featuremgmt.FlagQueryService, // Query Library
|
featuremgmt.FlagQueryService, // Query Library
|
||||||
featuremgmt.FlagProvisioning,
|
featuremgmt.FlagProvisioning,
|
||||||
featuremgmt.FlagInvestigationsBackend,
|
featuremgmt.FlagInvestigationsBackend,
|
||||||
|
|
|
@ -221,7 +221,6 @@ func runGrafana(t *testing.T, options ...grafanaOption) *provisioningTestHelper
|
||||||
AppModeProduction: false, // required for experimental APIs
|
AppModeProduction: false, // required for experimental APIs
|
||||||
EnableFeatureToggles: []string{
|
EnableFeatureToggles: []string{
|
||||||
featuremgmt.FlagProvisioning,
|
featuremgmt.FlagProvisioning,
|
||||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
|
||||||
},
|
},
|
||||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||||
"dashboards.dashboard.grafana.app": {
|
"dashboards.dashboard.grafana.app": {
|
||||||
|
|
|
@ -383,13 +383,8 @@ export class DashboardScene extends SceneObjectBase<DashboardSceneState> impleme
|
||||||
}
|
}
|
||||||
|
|
||||||
public onRestore = async (version: DecoratedRevisionModel): Promise<boolean> => {
|
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
|
// 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);
|
let versionRsp = await historySrv.restoreDashboard(version.uid, version.id);
|
||||||
} else {
|
|
||||||
versionRsp = await historySrv.restoreDashboard(version.uid, version.version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Number.isInteger(versionRsp.version)) {
|
if (!Number.isInteger(versionRsp.version)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { SceneTimeRange } from '@grafana/scenes';
|
import { SceneTimeRange } from '@grafana/scenes';
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
|
@ -149,10 +148,7 @@ describe('VersionsEditView', () => {
|
||||||
expect(versionsView.versions.find((rev) => rev.version === 1)).toBeUndefined();
|
expect(versionsView.versions.find((rev) => rev.version === 1)).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should correctly identify last page when kubernetesClientDashboardsFolders is enabled and continueToken is empty', async () => {
|
it('should correctly identify last page when continueToken is empty', async () => {
|
||||||
// @ts-ignore
|
|
||||||
config.featureToggles.kubernetesClientDashboardsFolders = true;
|
|
||||||
|
|
||||||
jest.mocked(historySrv.getHistoryList).mockResolvedValueOnce({
|
jest.mocked(historySrv.getHistoryList).mockResolvedValueOnce({
|
||||||
continueToken: '',
|
continueToken: '',
|
||||||
versions: [
|
versions: [
|
||||||
|
@ -190,10 +186,6 @@ describe('VersionsEditView', () => {
|
||||||
expect(versionsView.versions.length).toBeLessThan(VERSIONS_FETCH_LIMIT);
|
expect(versionsView.versions.length).toBeLessThan(VERSIONS_FETCH_LIMIT);
|
||||||
expect(versionsView.versions.find((rev) => rev.version === 1)).toBeUndefined();
|
expect(versionsView.versions.find((rev) => rev.version === 1)).toBeUndefined();
|
||||||
expect(versionsView.continueToken).toBe('');
|
expect(versionsView.continueToken).toBe('');
|
||||||
|
|
||||||
// reset feature flag
|
|
||||||
// @ts-ignore
|
|
||||||
config.featureToggles.kubernetesClientDashboardsFolders = false;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { PageLayoutType, dateTimeFormat, dateTimeFormatTimeAgo } from '@grafana/data';
|
import { PageLayoutType, dateTimeFormat, dateTimeFormatTimeAgo } from '@grafana/data';
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { SceneComponentProps, SceneObjectBase, sceneGraph } from '@grafana/scenes';
|
import { SceneComponentProps, SceneObjectBase, sceneGraph } from '@grafana/scenes';
|
||||||
import { Spinner, Stack } from '@grafana/ui';
|
import { Spinner, Stack } from '@grafana/ui';
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
|
@ -136,15 +135,9 @@ export class VersionsEditView extends SceneObjectBase<VersionsEditViewState> imp
|
||||||
if (!this._dashboard.state.uid) {
|
if (!this._dashboard.state.uid) {
|
||||||
return;
|
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
|
// 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);
|
let lhs = await historySrv.getDashboardVersion(this._dashboard.state.uid, baseInfo.id);
|
||||||
rhs = await historySrv.getDashboardVersion(this._dashboard.state.uid, newInfo.id);
|
let 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
baseInfo,
|
baseInfo,
|
||||||
|
@ -204,10 +197,10 @@ function VersionsEditorSettingsListView({ model }: SceneComponentProps<VersionsE
|
||||||
const showButtons = model.versions.length > 1;
|
const showButtons = model.versions.length > 1;
|
||||||
const hasMore = model.versions.length >= model.limit;
|
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
|
// 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;
|
let isLastPage =
|
||||||
if (config.featureToggles.kubernetesClientDashboardsFolders) {
|
model.versions.find((rev) => rev.version === 1) ||
|
||||||
isLastPage = isLastPage || model.continueToken === '';
|
model.versions.length % model.limit !== 0 ||
|
||||||
}
|
model.continueToken === '';
|
||||||
|
|
||||||
const viewModeCompare = (
|
const viewModeCompare = (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { screen, waitFor, within } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { render } from 'test/test-utils';
|
import { render } from 'test/test-utils';
|
||||||
|
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { historySrv } from 'app/features/dashboard-scene/settings/version-history/HistorySrv';
|
import { historySrv } from 'app/features/dashboard-scene/settings/version-history/HistorySrv';
|
||||||
|
|
||||||
import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures';
|
import { createDashboardModelFixture } from '../../state/__fixtures__/dashboardFixtures';
|
||||||
|
@ -110,7 +109,7 @@ describe('VersionSettings', () => {
|
||||||
|
|
||||||
test('renders buttons if versions >= VERSIONS_FETCH_LIMIT', async () => {
|
test('renders buttons if versions >= VERSIONS_FETCH_LIMIT', async () => {
|
||||||
historySrv.getHistoryList = jest.fn().mockResolvedValue({
|
historySrv.getHistoryList = jest.fn().mockResolvedValue({
|
||||||
continueToken: versions.continueToken,
|
continueToken: 'next-page-token',
|
||||||
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT),
|
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -135,13 +134,13 @@ describe('VersionSettings', () => {
|
||||||
.fn()
|
.fn()
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
continueToken: versions.continueToken,
|
continueToken: 'next-page-token',
|
||||||
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT),
|
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
continueToken: versions.continueToken,
|
continueToken: '',
|
||||||
versions: versions.versions.slice(VERSIONS_FETCH_LIMIT),
|
versions: versions.versions.slice(VERSIONS_FETCH_LIMIT),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -184,8 +183,7 @@ describe('VersionSettings', () => {
|
||||||
expect(screen.getByRole('button', { name: /compare versions/i })).toBeInTheDocument();
|
expect(screen.getByRole('button', { name: /compare versions/i })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('does not show more button when kubernetesClientDashboardsFolders is enabled and continueToken is empty', async () => {
|
test('does not show more button when continueToken is empty', async () => {
|
||||||
config.featureToggles.kubernetesClientDashboardsFolders = true;
|
|
||||||
historySrv.getHistoryList = jest.fn().mockResolvedValueOnce({
|
historySrv.getHistoryList = jest.fn().mockResolvedValueOnce({
|
||||||
continueToken: '',
|
continueToken: '',
|
||||||
versions: versions.versions.slice(0, VERSIONS_FETCH_LIMIT - 1),
|
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.queryByRole('button', { name: /show more versions/i })).not.toBeInTheDocument();
|
||||||
expect(screen.getByRole('button', { name: /compare versions/i })).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 () => {
|
test('selecting two versions and clicking compare button should render compare view', async () => {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { Spinner, HorizontalGroup } from '@grafana/ui';
|
import { Spinner, HorizontalGroup } from '@grafana/ui';
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
import { historySrv, RevisionsModel } from 'app/features/dashboard-scene/settings/version-history/HistorySrv';
|
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,
|
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
|
// 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);
|
let lhs = await historySrv.getDashboardVersion(this.props.dashboard.uid, baseInfo.id);
|
||||||
rhs = await historySrv.getDashboardVersion(this.props.dashboard.uid, newInfo.id);
|
let 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
baseInfo,
|
baseInfo,
|
||||||
|
@ -122,15 +115,12 @@ export class VersionsSettings extends PureComponent<Props, State> {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
isLastPage() {
|
isLastPage() {
|
||||||
if (config.featureToggles.kubernetesClientDashboardsFolders) {
|
|
||||||
return (
|
return (
|
||||||
this.state.versions.find((rev) => rev.version === 1) ||
|
this.state.versions.find((rev) => rev.version === 1) ||
|
||||||
this.state.versions.length % this.limit !== 0 ||
|
this.state.versions.length % this.limit !== 0 ||
|
||||||
this.continueToken === ''
|
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) => {
|
onCheck = (ev: React.FormEvent<HTMLInputElement>, versionId: number) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useEffect } from 'react';
|
||||||
import { useAsyncFn } from 'react-use';
|
import { useAsyncFn } from 'react-use';
|
||||||
|
|
||||||
import { locationUtil } from '@grafana/data';
|
import { locationUtil } from '@grafana/data';
|
||||||
import { config, locationService } from '@grafana/runtime';
|
import { locationService } from '@grafana/runtime';
|
||||||
import { useAppNotification } from 'app/core/copy/appNotification';
|
import { useAppNotification } from 'app/core/copy/appNotification';
|
||||||
import { historySrv } from 'app/features/dashboard-scene/settings/version-history/HistorySrv';
|
import { historySrv } from 'app/features/dashboard-scene/settings/version-history/HistorySrv';
|
||||||
import { useSelector } from 'app/types/store';
|
import { useSelector } from 'app/types/store';
|
||||||
|
@ -18,11 +18,7 @@ const restoreDashboard = async (version: number, dashboard: DashboardModel) => {
|
||||||
|
|
||||||
export const useDashboardRestore = (id: number, version: number) => {
|
export const useDashboardRestore = (id: number, version: number) => {
|
||||||
const dashboard = useSelector((state) => state.dashboard.getModel());
|
const dashboard = useSelector((state) => state.dashboard.getModel());
|
||||||
const [state, onRestoreDashboard] = useAsyncFn(
|
const [state, onRestoreDashboard] = useAsyncFn(async () => await restoreDashboard(id, dashboard!), []);
|
||||||
async () =>
|
|
||||||
await restoreDashboard(config.featureToggles.kubernetesClientDashboardsFolders ? id : version, dashboard!),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
const notifyApp = useAppNotification();
|
const notifyApp = useAppNotification();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import { FeatureToggles } from '@grafana/data';
|
import { FeatureToggles } from '@grafana/data';
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
|
|
||||||
export const requiredFeatureToggles: Array<keyof FeatureToggles> = [
|
export const requiredFeatureToggles: Array<keyof FeatureToggles> = ['provisioning', 'kubernetesDashboards'];
|
||||||
'provisioning',
|
|
||||||
'kubernetesDashboards',
|
|
||||||
'kubernetesClientDashboardsFolders',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if all required feature toggles are enabled
|
* Checks if all required feature toggles are enabled
|
||||||
|
|
Loading…
Reference in New Issue