diff --git a/.betterer.results b/.betterer.results index b889576f7fe..51766dc80c4 100644 --- a/.betterer.results +++ b/.betterer.results @@ -3308,9 +3308,6 @@ exports[`better eslint`] = { "public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx:5381": [ [0, 0, 0, "\'@grafana/data/src/text/sanitize\' import is restricted from being used by a pattern. Import from the public export instead.", "0"] ], - "public/app/features/dashboard-scene/scene/NavToolbarActions.tsx:5381": [ - [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] - ], "public/app/features/dashboard-scene/scene/PanelLinks.tsx:5381": [ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"] ], diff --git a/public/app/features/apiserver/types.ts b/public/app/features/apiserver/types.ts index 3c04564f8dd..6df2e903ca0 100644 --- a/public/app/features/apiserver/types.ts +++ b/public/app/features/apiserver/types.ts @@ -43,6 +43,13 @@ export const AnnoKeyFolderUrl = 'grafana.app/folderUrl'; export const AnnoKeyMessage = 'grafana.app/message'; export const AnnoKeySlug = 'grafana.app/slug'; +export enum ManagerKind { + Repo = 'repo', + Terraform = 'terraform', + Kubectl = 'kubectl', + Plugin = 'plugin', +} + export const AnnoKeyManagerKind = 'grafana.app/managedBy'; export const AnnoKeyManagerIdentity = 'grafana.app/managerId'; export const AnnoKeySourcePath = 'grafana.app/sourcePath'; @@ -66,7 +73,7 @@ type GrafanaAnnotations = { [AnnoKeyFolder]?: string; [AnnoKeySlug]?: string; - [AnnoKeyManagerKind]?: string; + [AnnoKeyManagerKind]?: ManagerKind; [AnnoKeyManagerIdentity]?: string; [AnnoKeySourcePath]?: string; [AnnoKeySourceChecksum]?: string; diff --git a/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx b/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx index 4de554e648f..30f99b64de0 100644 --- a/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx +++ b/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx @@ -59,7 +59,7 @@ export class SaveDashboardDrawer extends SceneObjectBase diff --git a/public/app/features/dashboard-scene/saving/provisioned/SaveProvisionedDashboard.tsx b/public/app/features/dashboard-scene/saving/provisioned/SaveProvisionedDashboard.tsx index 66327fdc54d..56fcac4528c 100644 --- a/public/app/features/dashboard-scene/saving/provisioned/SaveProvisionedDashboard.tsx +++ b/public/app/features/dashboard-scene/saving/provisioned/SaveProvisionedDashboard.tsx @@ -7,7 +7,7 @@ import { getAppEvents, locationService } from '@grafana/runtime'; import { Alert, Button, Field, Input, RadioButtonGroup, Stack, TextArea } from '@grafana/ui'; import { FolderPicker } from 'app/core/components/Select/FolderPicker'; import { useUrlParams } from 'app/core/navigation/hooks'; -import { AnnoKeyManagerIdentity } from 'app/features/apiserver/types'; +import { AnnoKeyManagerIdentity, ManagerKind } from 'app/features/apiserver/types'; import { validationSrv } from 'app/features/manage-dashboards/services/ValidationSrv'; import { RepositoryView } from 'app/features/provisioning/api'; import { PROVISIONING_URL } from 'app/features/provisioning/constants'; @@ -73,7 +73,7 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Prop if (request.isSuccess) { dashboard.setState({ isDirty: false }); if (isNew) { - dashboard.setRepoName(defaultValues.repo); + dashboard.setManager(ManagerKind.Repo, defaultValues.repo); } if (workflow === 'branch' && ref !== '' && path !== '') { // Redirect to the provisioning preview pages diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index 463d0027b70..81c0acacd8f 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -42,7 +42,7 @@ import { VariablesChanged } from 'app/features/variables/types'; import { DashboardDTO, DashboardMeta, KioskMode, SaveDashboardResponseDTO } from 'app/types'; import { ShowConfirmModalEvent } from 'app/types/events'; -import { AnnoKeyManagerIdentity, AnnoKeySourcePath } from '../../apiserver/types'; +import { AnnoKeyManagerIdentity, AnnoKeyManagerKind, AnnoKeySourcePath, ManagerKind } from '../../apiserver/types'; import { DashboardEditPane } from '../edit-pane/DashboardEditPane'; import { PanelEditor } from '../panel-edit/PanelEditor'; import { DashboardSceneChangeTracker } from '../saving/DashboardSceneChangeTracker'; @@ -743,25 +743,31 @@ export class DashboardScene extends SceneObjectBase impleme return this._serializer.getDashboardChangesFromScene(this, { saveTimeRange, saveVariables, saveRefresh }); } - /** - * Provisioned dashboards helpers - */ - getRepoName() { - return this.state.meta.k8s?.annotations?.[AnnoKeyManagerIdentity]; + getManagerKind(): ManagerKind | undefined { + return this.state.meta.k8s?.annotations?.[AnnoKeyManagerKind]; } - isProvisioned() { - return Boolean(this.getRepoName()); + isManaged() { + return Boolean(this.getManagerKind()); + } + + isManagedRepository() { + return Boolean(this.getManagerKind() === ManagerKind.Repo); } getPath() { return this.state.meta.k8s?.annotations?.[AnnoKeySourcePath]; } - setRepoName(name: string) { + setManager(kind: ManagerKind, id: string) { this.setState({ meta: { - k8s: { annotations: { [AnnoKeyManagerIdentity]: name } }, + k8s: { + annotations: { + [AnnoKeyManagerKind]: kind, + [AnnoKeyManagerIdentity]: id, + }, + }, }, }); } diff --git a/public/app/features/dashboard-scene/scene/ManagedDashboardNavBarBadge.tsx b/public/app/features/dashboard-scene/scene/ManagedDashboardNavBarBadge.tsx new file mode 100644 index 00000000000..9594e1c1eb3 --- /dev/null +++ b/public/app/features/dashboard-scene/scene/ManagedDashboardNavBarBadge.tsx @@ -0,0 +1,23 @@ +import { Badge } from '@grafana/ui'; +import { AnnoKeyManagerIdentity, AnnoKeyManagerKind, ManagerKind } from 'app/features/apiserver/types'; +import { DashboardMeta } from 'app/types'; + +export default function ManagedDashboardNavBarBadge({ meta }: { meta: DashboardMeta }) { + const obj = meta.k8s; + if (!obj?.annotations) { + return; + } + + let text = 'Provisioned'; + const kind = obj.annotations?.[AnnoKeyManagerKind]; + const id = obj.annotations?.[AnnoKeyManagerIdentity]; + switch (kind) { + case ManagerKind.Terraform: + text = 'Terraform'; + case ManagerKind.Kubectl: + text = 'Kubectl'; + case ManagerKind.Plugin: + text = `Plugin: ${id}`; + } + return ; +} diff --git a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx index c8358904793..5c509293dcb 100644 --- a/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx +++ b/public/app/features/dashboard-scene/scene/NavToolbarActions.tsx @@ -23,6 +23,7 @@ import { NavToolbarSeparator } from 'app/core/components/AppChrome/NavToolbar/Na import { LS_PANEL_COPY_KEY } from 'app/core/constants'; import { contextSrv } from 'app/core/core'; import { Trans, t } from 'app/core/internationalization'; +import { AnnoKeyManagerIdentity, AnnoKeyManagerKind, ManagerKind } from 'app/features/apiserver/types'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { playlistSrv } from 'app/features/playlist/PlaylistSrv'; import { ScopesSelector } from 'app/features/scopes'; @@ -39,6 +40,7 @@ import { isLibraryPanel } from '../utils/utils'; import { DashboardScene } from './DashboardScene'; import { GoToSnapshotOriginButton } from './GoToSnapshotOriginButton'; +import ManagedDashboardNavBarBadge from './ManagedDashboardNavBarBadge'; interface Props { dashboard: DashboardScene; @@ -71,7 +73,7 @@ export function ToolbarActions({ dashboard }: Props) { const isViewingPanel = Boolean(viewPanelScene); const isEditedPanelDirty = usePanelEditDirty(editPanel); const isEditingLibraryPanel = editPanel && isLibraryPanel(editPanel.state.panelRef.resolve()); - const isNew = !Boolean(uid || dashboard.getRepoName()); + const isNew = !Boolean(uid || dashboard.isManaged()); const hasCopiedPanel = store.exists(LS_PANEL_COPY_KEY); // Means we are not in settings view, fullscreen panel or edit panel @@ -80,7 +82,7 @@ export function ToolbarActions({ dashboard }: Props) { const showScopesSelector = config.featureToggles.scopeFilters && !isEditing; const dashboardNewLayouts = config.featureToggles.dashboardNewLayouts; const folderRepo = useSelector((state) => selectFolderRepository(state, meta.folderUid)); - const isProvisionedNG = Boolean(dashboard.isProvisioned() || folderRepo); + const isManaged = Boolean(dashboard.isManaged() || folderRepo); if (!isEditingPanel) { // This adds the presence indicators in enterprise @@ -129,14 +131,12 @@ export function ToolbarActions({ dashboard }: Props) { }); } - if (isProvisionedNG) { + if (isManaged && meta.canEdit) { toolbarActions.push({ group: 'icon-actions', condition: true, render: () => { - return ( - - ); + return ; }, }); } @@ -564,7 +564,7 @@ export function ToolbarActions({ dashboard }: Props) { } // If we only can save as copy - if (canSaveAs && !meta.canSave && !meta.canMakeEditable && !isProvisionedNG) { + if (canSaveAs && !meta.canSave && !meta.canMakeEditable && !isManaged) { return (