diff --git a/public/app/features/dashboard-scene/saving/SaveProvisionedDashboard.tsx b/public/app/features/dashboard-scene/saving/SaveProvisionedDashboard.tsx index d7320c3efc7..6de5ca4c4c2 100644 --- a/public/app/features/dashboard-scene/saving/SaveProvisionedDashboard.tsx +++ b/public/app/features/dashboard-scene/saving/SaveProvisionedDashboard.tsx @@ -8,7 +8,7 @@ import { AnnoKeyRepoName, AnnoKeyRepoPath } from 'app/features/apiserver/types'; import { DashboardMeta } from 'app/types'; import { useGetRepositoryQuery, useUpdateRepositoryFilesMutation } from '../../provisioning/api'; -import { RepositorySpec } from '../../provisioning/api/types'; +import { createPRLink, validateBranchName } from '../../provisioning/utils/git'; import { DashboardScene } from '../scene/DashboardScene'; import { SaveDashboardDrawer } from './SaveDashboardDrawer'; @@ -35,13 +35,6 @@ function getDefaultValues(meta: DashboardMeta) { return { ref, path, repo, comment: '' }; } -function createPRLink(spec?: RepositorySpec, dashboardName?: string, ref?: string) { - if (!spec || spec.type !== 'github' || !ref) { - return ''; - } - return `https://github.com/${spec.github?.owner}/${spec.github?.repository}/compare/${spec.github?.branch}...${ref}?quick_pull=1&labels=grafana&title=Update dashboard ${dashboardName}`; -} - export interface Props { meta: DashboardMeta; dashboard: DashboardScene; @@ -52,7 +45,12 @@ export interface Props { export function SaveProvisionedDashboard({ meta, drawer, changeInfo, dashboard }: Props) { const [saveDashboard, request] = useUpdateRepositoryFilesMutation(); const defaultValues = getDefaultValues(meta); - const { register, handleSubmit, watch } = useForm({ defaultValues }); + const { + register, + handleSubmit, + watch, + formState: { errors }, + } = useForm({ defaultValues }); const repositoryConfigQuery = useGetRepositoryQuery({ name: defaultValues.repo }); const repositoryConfig = repositoryConfigQuery?.data?.spec; const isGitHub = repositoryConfig?.type === 'github'; @@ -104,8 +102,13 @@ export function SaveProvisionedDashboard({ meta, drawer, changeInfo, dashboard } {isGitHub && ( - - + : ''} + > + )} @@ -136,3 +139,16 @@ export function SaveProvisionedDashboard({ meta, drawer, changeInfo, dashboard } ); } +const BranchValidationError = () => { + return ( + <> + Invalid branch name. + + + ); +}; diff --git a/public/app/features/provisioning/utils/git.ts b/public/app/features/provisioning/utils/git.ts new file mode 100644 index 00000000000..f00b6b9e9fb --- /dev/null +++ b/public/app/features/provisioning/utils/git.ts @@ -0,0 +1,23 @@ +import { RepositorySpec } from '../api/types'; + +export function createPRLink(spec?: RepositorySpec, dashboardName?: string, ref?: string) { + if (!spec || spec.type !== 'github' || !ref) { + return ''; + } + return `https://github.com/${spec.github?.owner}/${spec.github?.repository}/compare/${spec.github?.branch}...${ref}?quick_pull=1&labels=grafana&title=Update dashboard ${dashboardName}`; +} + +/** + * Validates a Git branch name according to the following rules: + * 1. The branch name cannot start with `/`, end with `/`, `.`, or whitespace. + * 2. The branch name cannot contain consecutive slashes (`//`). + * 3. The branch name cannot contain consecutive dots (`..`). + * 4. The branch name cannot contain `@{`. + * 5. The branch name cannot include the following characters: `~`, `^`, `:`, `?`, `*`, `[`, `\`, or `]`. + * 6. The branch name must have at least one character and must not be empty. + */ +export function validateBranchName(branchName: string) { + const branchNameRegex = /^(?!\/|.*\/\/|.*\.\.|.*@{)(?!.*[~^:?*[\]\\]).+(?