mirror of https://github.com/grafana/grafana
Provisioning: return field paths in test error messages (#103850)
* Provisioning: Do not block connect step on error * Display field errors * Cleanup * return field errors * fix test * convert errros to an array * Fix history display * Add getFormErrors * metav1 issues * lint * Proper field names * Fix notification * Remove unused component --------- Co-authored-by: Clarity-89 <homes89@ukr.net>pull/103818/head
parent
ed9a7e8d9f
commit
2c3422fc5c
@ -1,16 +0,0 @@ |
||||
import { Job } from 'app/api/clients/provisioning'; |
||||
|
||||
import { JobContent } from './JobContent'; |
||||
import { useJobStatusEffect } from './hooks'; |
||||
|
||||
export interface ActiveJobProps { |
||||
job: Job; |
||||
onStatusChange?: (success: boolean) => void; |
||||
onRunningChange?: (isRunning: boolean) => void; |
||||
onErrorChange?: (error: string | null) => void; |
||||
} |
||||
|
||||
export function ActiveJobStatus({ job, onStatusChange, onRunningChange, onErrorChange }: ActiveJobProps) { |
||||
useJobStatusEffect(job, onStatusChange, onRunningChange, onErrorChange); |
||||
return <JobContent job={job} isFinishedJob={false} />; |
||||
} |
@ -1,31 +0,0 @@ |
||||
import { useEffect } from 'react'; |
||||
|
||||
import { Job } from 'app/api/clients/provisioning'; |
||||
import { t } from 'app/core/internationalization'; |
||||
|
||||
// Shared hook for status change effects
|
||||
export function useJobStatusEffect( |
||||
job?: Job, |
||||
onStatusChange?: (success: boolean) => void, |
||||
onRunningChange?: (isRunning: boolean) => void, |
||||
onErrorChange?: (error: string | null) => void |
||||
) { |
||||
useEffect(() => { |
||||
if (!job) { |
||||
return; |
||||
} |
||||
|
||||
if (onStatusChange && job.status?.state === 'success') { |
||||
onStatusChange(true); |
||||
if (onRunningChange) { |
||||
onRunningChange(false); |
||||
} |
||||
} |
||||
if (onErrorChange && job.status?.state === 'error') { |
||||
onErrorChange(job.status.message ?? t('provisioning.job-status.error-unknown', 'An unknown error occurred')); |
||||
if (onRunningChange) { |
||||
onRunningChange(false); |
||||
} |
||||
} |
||||
}, [job, onStatusChange, onErrorChange, onRunningChange]); |
||||
} |
@ -1,40 +0,0 @@ |
||||
import { Alert } from '@grafana/ui'; |
||||
import { t } from 'app/core/internationalization'; |
||||
import { getMessageFromError } from 'app/core/utils/errors'; |
||||
|
||||
interface RequestErrorAlertProps { |
||||
request?: { |
||||
isError: boolean; |
||||
error?: unknown; |
||||
endpointName?: string; |
||||
} | null; |
||||
title?: string; |
||||
} |
||||
|
||||
function getDefaultTitle(endpointName?: string): string { |
||||
switch (endpointName) { |
||||
case 'createRepositorySync': |
||||
return t('provisioning.request-error.failed-to-sync', 'Failed to sync dashboards'); |
||||
case 'createRepositoryMigrate': |
||||
return t('provisioning.request-error.failed-to-migrate', 'Failed to migrate dashboards'); |
||||
case 'createOrUpdateRepository': |
||||
return t('provisioning.request-error.failed-to-save', 'Failed to save repository'); |
||||
default: |
||||
return t('provisioning.request-error.operation-failed', 'Operation failed'); |
||||
} |
||||
} |
||||
|
||||
export function RequestErrorAlert({ request, title }: RequestErrorAlertProps) { |
||||
if (!request || !request.isError) { |
||||
return null; |
||||
} |
||||
|
||||
const errorTitle = title || getDefaultTitle(request.endpointName); |
||||
const errorMessage = getMessageFromError(request.error); |
||||
|
||||
return ( |
||||
<Alert severity="error" title={errorTitle}> |
||||
{errorMessage} |
||||
</Alert> |
||||
); |
||||
} |
@ -0,0 +1,39 @@ |
||||
import { ErrorDetails } from 'app/api/clients/provisioning'; |
||||
|
||||
import { WizardFormData } from '../Wizard/types'; |
||||
|
||||
export type RepositoryField = keyof WizardFormData['repository']; |
||||
export type RepositoryFormPath = `repository.${RepositoryField}`; |
||||
export type FormErrorTuple = [RepositoryFormPath | null, { message: string } | null]; |
||||
|
||||
/** |
||||
* Maps API error details to form error fields for React Hook Form |
||||
* |
||||
* @param errors Array of error details from the API response |
||||
* @returns Tuple with form field path and error message |
||||
*/ |
||||
export const getFormErrors = (errors: ErrorDetails[]): FormErrorTuple => { |
||||
const fieldsToValidate = ['local.path', 'github.branch', 'github.url', 'github.token']; |
||||
const fieldMap: Record<string, RepositoryFormPath> = { |
||||
path: 'repository.path', |
||||
branch: 'repository.branch', |
||||
url: 'repository.url', |
||||
token: 'repository.token', |
||||
}; |
||||
|
||||
for (const error of errors) { |
||||
if (error.field) { |
||||
const cleanField = error.field.replace('spec.', ''); |
||||
if (fieldsToValidate.includes(cleanField)) { |
||||
const fieldParts = cleanField.split('.'); |
||||
const lastPart = fieldParts[fieldParts.length - 1]; |
||||
|
||||
if (lastPart in fieldMap) { |
||||
return [fieldMap[lastPart], { message: error.detail || `Invalid ${lastPart}` }]; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return [null, null]; |
||||
}; |
Loading…
Reference in new issue