From 366121b355b44cc4e688060d3f8b9ea9c4c6b865 Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Fri, 29 Nov 2024 12:32:35 +0200 Subject: [PATCH] POC/Provisioning: Save dashboard (#97167) * Create provisioned db * Redirect to preview * Enable import for all * Add "Provisioned" badge * Add "Provisioned" badge * Do not show save for provisioned db * Show existing PRs * Betterer --- .betterer.results | 3 +- .../saving/SaveDashboardDrawer.tsx | 6 +- .../saving/SaveProvisionedDashboard.tsx | 92 +++++++++++++++---- .../dashboard-scene/scene/DashboardScene.tsx | 11 ++- .../scene/NavToolbarActions.tsx | 20 ++++ .../provisioning/ImportFromRepository.tsx | 4 - .../provisioning/RepositorySelect.tsx | 25 +++++ public/app/features/provisioning/hooks.ts | 38 +++++++- 8 files changed, 171 insertions(+), 28 deletions(-) create mode 100644 public/app/features/provisioning/RepositorySelect.tsx diff --git a/.betterer.results b/.betterer.results index c9e38b92ea1..f6934188d82 100644 --- a/.betterer.results +++ b/.betterer.results @@ -2074,7 +2074,8 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "8"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "8"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "9"] ], "public/app/features/dashboard-scene/saving/SaveProvisionedDashboardForm.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], diff --git a/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx b/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx index 2069179469b..5f12ce0c39e 100644 --- a/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx +++ b/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx @@ -19,6 +19,7 @@ interface SaveDashboardDrawerState extends SceneObjectState { saveRefresh?: boolean; saveAsCopy?: boolean; onSaveSuccess?: () => void; + saveProvisioned?: boolean; } export class SaveDashboardDrawer extends SceneObjectBase { @@ -39,7 +40,7 @@ export class SaveDashboardDrawer extends SceneObjectBase) => { - const { showDiff, saveAsCopy, saveTimeRange, saveVariables, saveRefresh } = model.useState(); + const { saveProvisioned, showDiff, saveAsCopy, saveTimeRange, saveVariables, saveRefresh } = model.useState(); const changeInfo = getDashboardChangesFromScene( model.state.dashboardRef.resolve(), saveTimeRange, @@ -51,9 +52,8 @@ export class SaveDashboardDrawer extends SceneObjectBase diff --git a/public/app/features/dashboard-scene/saving/SaveProvisionedDashboard.tsx b/public/app/features/dashboard-scene/saving/SaveProvisionedDashboard.tsx index 56b68d7dcf3..35a777b8889 100644 --- a/public/app/features/dashboard-scene/saving/SaveProvisionedDashboard.tsx +++ b/public/app/features/dashboard-scene/saving/SaveProvisionedDashboard.tsx @@ -1,13 +1,29 @@ import { useEffect } from 'react'; import { Controller, useForm } from 'react-hook-form'; +import { useNavigate } from 'react-router-dom-v5-compat'; import { AppEvents } from '@grafana/data'; import { getAppEvents } from '@grafana/runtime'; -import { Alert, Button, Field, Input, LinkButton, RadioButtonGroup, Stack, TextArea } from '@grafana/ui'; +import { + Alert, + Button, + Field, + Input, + LinkButton, + RadioButtonGroup, + Spinner, + Stack, + TextArea, + TextLink, +} from '@grafana/ui'; import { AnnoKeyRepoName, AnnoKeyRepoPath } from 'app/features/apiserver/types'; import { DashboardMeta } from 'app/types'; -import { useGetRepositoryQuery, useUpdateRepositoryFilesMutation } from '../../provisioning/api'; +import { RepositorySelect } from '../../provisioning/RepositorySelect'; +import { useGetRepositoryQuery } from '../../provisioning/api'; +import { RepositorySpec } from '../../provisioning/api/types'; +import { PROVISIONING_URL } from '../../provisioning/constants'; +import { useCreateOrUpdateRepositoryFile, usePullRequestParam } from '../../provisioning/hooks'; import { WorkflowOption } from '../../provisioning/types'; import { createPRLink, validateBranchName } from '../../provisioning/utils/git'; import { DashboardScene } from '../scene/DashboardScene'; @@ -27,7 +43,7 @@ type FormData = { function getDefaultValues(meta: DashboardMeta) { const anno = meta.k8s?.annotations; let ref = ''; - let path = anno?.[AnnoKeyRepoPath] ?? ''; + let path = anno?.[AnnoKeyRepoPath] ?? `${meta.slug}.json` ?? ''; const repo = anno?.[AnnoKeyRepoName] ?? ''; const idx = path.indexOf('#'); if (idx > 0) { @@ -42,6 +58,10 @@ function getDefaultValues(meta: DashboardMeta) { }; } +const getDefaultWorkflow = (config?: RepositorySpec) => { + return config?.github?.branchWorkflow ? WorkflowOption.Branch : WorkflowOption.PullRequest; +}; + function getWorkflowOptions(branch = 'main') { return [ { label: `Commit to ${branch}`, value: WorkflowOption.Branch }, @@ -57,26 +77,31 @@ export interface Props { } export function SaveProvisionedDashboard({ meta, drawer, changeInfo, dashboard }: Props) { - const [saveDashboard, request] = useUpdateRepositoryFilesMutation(); + // Saving as a new provisioned dashboard + const { saveProvisioned } = drawer.useState(); + const prURL = usePullRequestParam(); const defaultValues = getDefaultValues(meta); - const repositoryConfigQuery = useGetRepositoryQuery({ name: defaultValues.repo }); - const repositoryConfig = repositoryConfigQuery?.data?.spec; - const isGitHub = repositoryConfig?.type === 'github'; + const [action, request] = useCreateOrUpdateRepositoryFile(saveProvisioned ? undefined : defaultValues.path); const { register, handleSubmit, watch, formState: { errors }, control, + setValue, } = useForm({ defaultValues: { ...defaultValues, - workflow: repositoryConfig?.github?.branchWorkflow ? WorkflowOption.Branch : WorkflowOption.PullRequest, + workflow: WorkflowOption.PullRequest, }, }); - const [repo, ref, workflow] = watch(['repo', 'ref', 'workflow']); + const [repo, ref, workflow, path] = watch(['repo', 'ref', 'workflow', 'path']); + const repositoryConfigQuery = useGetRepositoryQuery({ name: repo }); + const repositoryConfig = repositoryConfigQuery?.data?.spec; + const isGitHub = repositoryConfig?.type === 'github'; const href = createPRLink(repositoryConfig, repo, ref); const { isDirty } = dashboard.state; + const navigate = useNavigate(); useEffect(() => { const appEvents = getAppEvents(); @@ -86,13 +111,21 @@ export function SaveProvisionedDashboard({ meta, drawer, changeInfo, dashboard } payload: ['Dashboard saved'], }); dashboard.setState({ isDirty: false }); + + if (saveProvisioned) { + navigate(`${PROVISIONING_URL}/${repo}/dashboard/preview/${path}?ref=${ref}`); + } } else if (request.isError) { appEvents.publish({ type: AppEvents.alertError.name, payload: ['Error saving dashboard', request.error], }); } - }, [request.isSuccess, request.isError, request.error, dashboard]); + }, [request.isSuccess, request.isError, request.error, dashboard, path, saveProvisioned, navigate, repo, ref]); + + useEffect(() => { + setValue('workflow', getDefaultWorkflow(repositoryConfig)); + }, [repositoryConfig, setValue]); const doSave = ({ ref, path, comment, repo }: FormData) => { if (!repo || !path) { @@ -102,9 +135,16 @@ export function SaveProvisionedDashboard({ meta, drawer, changeInfo, dashboard } if (workflow === WorkflowOption.Branch) { ref = repositoryConfig?.github?.branch || 'main'; } - saveDashboard({ ref, name: repo, path, message: comment, body: changeInfo.changedSaveModel }); + action({ ref, name: repo, path, message: comment, body: changeInfo.changedSaveModel }); }; + if (repositoryConfigQuery.isLoading) { + return ( + + + + ); + } return (
@@ -117,11 +157,21 @@ export function SaveProvisionedDashboard({ meta, drawer, changeInfo, dashboard } - + {saveProvisioned ? ( + { + return onChange(v.value)} />; + }} + /> + ) : ( + + )} - + {isGitHub && ( @@ -130,9 +180,9 @@ export function SaveProvisionedDashboard({ meta, drawer, changeInfo, dashboard } ( - - )} + render={({ field: { ref, ...field } }) => { + return ; + }} /> {workflow === WorkflowOption.PullRequest && ( @@ -164,8 +214,16 @@ export function SaveProvisionedDashboard({ meta, drawer, changeInfo, dashboard } )} + {prURL && ( + + A pull request has been created with changes to this dashboard:{' '} + + {prURL} + + + )} -