Datasource: Overhaul plugin error handling and action buttons (#67014)

* - initial work on data source config page

* - add links to test status box
- add tracking function

* - add test for the DataSourceConfigAlert component

* - fix flicker of the alert box

* - fix the build

* - small improvements

* - fix failing build

* - fix failing unit tests

* - prettier and betterer fixes

* - fix failing e2e tests

* - fix build again

* - rewrite solution according to the PR comments

* - cleanup

* - fix failing e2e

* - use absolute path in link

* Minor fixes

---------

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
Kuba Siemiatkowski
2023-04-28 15:57:17 +02:00
committed by GitHub
parent fe59b65f9e
commit f8faacd54a
22 changed files with 262 additions and 95 deletions

View File

@@ -57,6 +57,40 @@ export interface TestDataSourceDependencies {
getBackendSrv: typeof getBackendSrv;
}
type parseDataSourceSaveResponse = {
message?: string | undefined;
status?: string;
details?: HealthCheckResultDetails | { message?: string; verboseMessage?: string };
};
const parseHealthCheckError = (errorResponse: any): parseDataSourceSaveResponse => {
let message: string | undefined;
let details: HealthCheckResultDetails;
if (errorResponse.error && errorResponse.error instanceof HealthCheckError) {
message = errorResponse.error.message;
details = errorResponse.error.details;
} else if (isFetchError(errorResponse)) {
message = errorResponse.data.message ?? `HTTP error ${errorResponse.statusText}`;
} else if (errorResponse instanceof Error) {
message = errorResponse.message;
}
return { message, details };
};
const parseHealthCheckSuccess = (response: any): parseDataSourceSaveResponse => {
let message: string | undefined;
let status: string;
let details: { message?: string; verboseMessage?: string };
status = response.status;
message = response.message;
details = response.details;
return { status, message, details };
};
export const initDataSourceSettings = (
uid: string,
dependencies: InitDataSourceSettingDependencies = {
@@ -112,7 +146,9 @@ export const testDataSource = (
try {
const result = await dsApi.testDatasource();
dispatch(testDataSourceSucceeded(result));
const parsedResult = parseHealthCheckSuccess({ ...result, details: { ...result.details } });
dispatch(testDataSourceSucceeded(parsedResult));
trackDataSourceTested({
grafana_version: config.buildInfo.version,
plugin_id: dsApi.type,
@@ -121,19 +157,9 @@ export const testDataSource = (
path: editLink,
});
} catch (err) {
let message: string | undefined;
let details: HealthCheckResultDetails;
const formattedError = parseHealthCheckError(err);
if (err instanceof HealthCheckError) {
message = err.message;
details = err.details;
} else if (isFetchError(err)) {
message = err.data.message ?? `HTTP error ${err.statusText}`;
} else if (err instanceof Error) {
message = err.message;
}
dispatch(testDataSourceFailed({ message, details }));
dispatch(testDataSourceFailed({ ...formattedError }));
trackDataSourceTested({
grafana_version: config.buildInfo.version,
plugin_id: dsApi.type,
@@ -216,6 +242,7 @@ export function addDataSource(
isDefault: isFirstDataSource,
};
// TODO: typo in name
if (nameExits(dataSources, newInstance.name)) {
newInstance.name = findNewName(dataSources, newInstance.name);
}
@@ -248,9 +275,23 @@ export function loadDataSourcePlugins(): ThunkResult<void> {
}
export function updateDataSource(dataSource: DataSourceSettings) {
return async (dispatch: (dataSourceSettings: ThunkResult<Promise<DataSourceSettings>>) => DataSourceSettings) => {
await api.updateDataSource(dataSource);
return async (
dispatch: (
dataSourceSettings: ThunkResult<Promise<DataSourceSettings>> | { payload: unknown; type: string }
) => DataSourceSettings
) => {
try {
await api.updateDataSource(dataSource);
} catch (err: any) {
const formattedError = parseHealthCheckError(err);
dispatch(testDataSourceFailed(formattedError));
return Promise.reject(dataSource);
}
await getDatasourceSrv().reload();
return dispatch(loadDataSource(dataSource.uid));
};
}
@@ -259,13 +300,18 @@ export function deleteLoadedDataSource(): ThunkResult<void> {
return async (dispatch, getStore) => {
const { uid } = getStore().dataSources.dataSource;
await api.deleteDataSource(uid);
await getDatasourceSrv().reload();
try {
await api.deleteDataSource(uid);
await getDatasourceSrv().reload();
const datasourcesUrl = config.featureToggles.dataConnectionsConsole
? CONNECTIONS_ROUTES.DataSources
: '/datasources';
const datasourcesUrl = config.featureToggles.dataConnectionsConsole
? CONNECTIONS_ROUTES.DataSources
: '/datasources';
locationService.push(datasourcesUrl);
locationService.push(datasourcesUrl);
} catch (err) {
const formattedError = parseHealthCheckError(err);
dispatch(testDataSourceFailed(formattedError));
}
};
}