diff --git a/public/app/features/browse-dashboards/api/browseDashboardsAPI.ts b/public/app/features/browse-dashboards/api/browseDashboardsAPI.ts index addc640cef9..62094c7856e 100644 --- a/public/app/features/browse-dashboards/api/browseDashboardsAPI.ts +++ b/public/app/features/browse-dashboards/api/browseDashboardsAPI.ts @@ -4,6 +4,7 @@ import { AppEvents, isTruthy, locationUtil } from '@grafana/data'; import { config, getBackendSrv, locationService } from '@grafana/runtime'; import { Dashboard } from '@grafana/schema'; import { Spec as DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen'; +import { folderAPI } from 'app/api/clients/folder'; import { createBaseQuery, handleRequestError } from 'app/api/createBaseQuery'; import appEvents from 'app/core/app_events'; import { contextSrv } from 'app/core/core'; @@ -11,6 +12,7 @@ import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api'; import { isDashboardV2Resource, isV1DashboardCommand, isV2DashboardCommand } from 'app/features/dashboard/api/utils'; import { SaveDashboardCommand } from 'app/features/dashboard/components/SaveDashboard/types'; import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher'; +import { dispatch } from 'app/store/store'; import { DescendantCount, DescendantCountDTO, @@ -25,6 +27,7 @@ import { t } from '../../../core/internationalization'; import { refetchChildren, refreshParents } from '../state'; import { DashboardTreeSelection } from '../types'; +import { isProvisionedDashboard, isProvisionedFolder } from './isProvisioned'; import { PAGE_SIZE } from './services'; interface DeleteItemsArgs { @@ -229,6 +232,17 @@ export const browseDashboardsAPI = createApi({ // Move all the folders sequentially // TODO error handling here for (const folderUID of selectedFolders) { + if (config.featureToggles.provisioning) { + const folder = await dispatch(folderAPI.endpoints.getFolder.initiate({ name: folderUID })); + if (isProvisionedFolder(folder.data)) { + appEvents.publish({ + type: AppEvents.alertWarning.name, + payload: ['Cannot move provisioned folder'], + }); + continue; + } + } + await baseQuery({ url: `/folders/${folderUID}/move`, method: 'POST', @@ -242,6 +256,16 @@ export const browseDashboardsAPI = createApi({ const fullDash = await getDashboardAPI().getDashboardDTO(dashboardUID); const dashboard = isDashboardV2Resource(fullDash) ? fullDash.spec : fullDash.dashboard; const k8s = isDashboardV2Resource(fullDash) ? fullDash.metadata : undefined; + + if (config.featureToggles.provisioning) { + if (isProvisionedDashboard(fullDash)) { + appEvents.publish({ + type: AppEvents.alertWarning.name, + payload: ['Cannot move provisioned dashboard'], + }); + continue; + } + } await getDashboardAPI().saveDashboard({ dashboard, folderUid: destinationUID, @@ -276,6 +300,18 @@ export const browseDashboardsAPI = createApi({ // Delete all the folders sequentially // TODO error handling here for (const folderUID of selectedFolders) { + if (config.featureToggles.provisioning) { + const folder = await dispatch(folderAPI.endpoints.getFolder.initiate({ name: folderUID })); + if (isProvisionedFolder(folder.data)) { + appEvents.publish({ + type: AppEvents.alertWarning.name, + payload: [ + 'Cannot delete provisioned folder. To remove it, delete it from the repository and synchronise to apply the changes.', + ], + }); + continue; + } + } await baseQuery({ url: `/folders/${folderUID}`, method: 'DELETE', @@ -289,6 +325,20 @@ export const browseDashboardsAPI = createApi({ // Delete all the dashboards sequentially // TODO error handling here for (const dashboardUID of selectedDashboards) { + if (config.featureToggles.provisioning) { + const dto = await getDashboardAPI().getDashboardDTO(dashboardUID); + if (isProvisionedDashboard(dto)) { + appEvents.publish({ + type: AppEvents.alertWarning.name, + payload: [ + 'Cannot delete provisioned dashboard. To remove it, delete it from the repository and synchronise to apply the changes.', + ], + }); + + continue; + } + } + await getDashboardAPI().deleteDashboard(dashboardUID, true); // handling success alerts for these feature toggles diff --git a/public/app/features/browse-dashboards/api/isProvisioned.ts b/public/app/features/browse-dashboards/api/isProvisioned.ts new file mode 100644 index 00000000000..a036f8fcd1e --- /dev/null +++ b/public/app/features/browse-dashboards/api/isProvisioned.ts @@ -0,0 +1,14 @@ +import { Folder } from 'app/api/clients/folder'; +import { AnnoKeyManagerKind, ManagerKind } from 'app/features/apiserver/types'; +import { DashboardDTO } from 'app/types'; + +import { DashboardWithAccessInfo } from '../../dashboard/api/types'; + +export function isProvisionedDashboard(dashboard: DashboardDTO | DashboardWithAccessInfo) { + const annotations = 'meta' in dashboard ? dashboard.meta.k8s?.annotations : dashboard.metadata.annotations; + return annotations?.[AnnoKeyManagerKind] === ManagerKind.Repo; +} + +export function isProvisionedFolder(folder: Folder) { + return folder.metadata.annotations?.[AnnoKeyManagerKind] === ManagerKind.Repo; +}