Query History: Track query history migration failures (#49560)

This commit is contained in:
Piotr Jamróz 2022-05-25 12:21:23 +02:00 committed by GitHub
parent f93ad85b08
commit a67add5239
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 27 additions and 10 deletions

View File

@ -15,6 +15,7 @@ import {
deleteQueryInRichHistory, deleteQueryInRichHistory,
migrateQueryHistoryFromLocalStorage, migrateQueryHistoryFromLocalStorage,
SortOrder, SortOrder,
LocalStorageMigrationStatus,
} from './richHistory'; } from './richHistory';
const richHistoryStorageMock: RichHistoryStorage = {} as RichHistoryStorage; const richHistoryStorageMock: RichHistoryStorage = {} as RichHistoryStorage;
@ -187,13 +188,23 @@ describe('richHistory', () => {
const history = { richHistory: [{ id: 'test' }, { id: 'test2' }], total: 2 }; const history = { richHistory: [{ id: 'test' }, { id: 'test2' }], total: 2 };
richHistoryLocalStorageMock.getRichHistory.mockReturnValue(history); richHistoryLocalStorageMock.getRichHistory.mockReturnValue(history);
await migrateQueryHistoryFromLocalStorage(); const migrationResult = await migrateQueryHistoryFromLocalStorage();
expect(richHistoryRemoteStorageMock.migrate).toBeCalledWith(history.richHistory); expect(richHistoryRemoteStorageMock.migrate).toBeCalledWith(history.richHistory);
expect(migrationResult.status).toBe(LocalStorageMigrationStatus.Successful);
expect(migrationResult.error).toBeUndefined();
}); });
it('does not migrate if there are no entries', async () => { it('does not migrate if there are no entries', async () => {
richHistoryLocalStorageMock.getRichHistory.mockReturnValue([]); richHistoryLocalStorageMock.getRichHistory.mockReturnValue({ richHistory: [] });
await migrateQueryHistoryFromLocalStorage(); const migrationResult = await migrateQueryHistoryFromLocalStorage();
expect(richHistoryRemoteStorageMock.migrate).not.toBeCalled(); expect(richHistoryRemoteStorageMock.migrate).not.toBeCalled();
expect(migrationResult.status).toBe(LocalStorageMigrationStatus.NotNeeded);
expect(migrationResult.error).toBeUndefined();
});
it('propagates thrown errors', async () => {
richHistoryLocalStorageMock.getRichHistory.mockRejectedValue(new Error('migration failed'));
const migrationResult = await migrateQueryHistoryFromLocalStorage();
expect(migrationResult.status).toBe(LocalStorageMigrationStatus.Failed);
expect(migrationResult.error?.message).toBe('migration failed');
}); });
}); });

View File

@ -131,7 +131,12 @@ export enum LocalStorageMigrationStatus {
NotNeeded = 'not-needed', NotNeeded = 'not-needed',
} }
export async function migrateQueryHistoryFromLocalStorage(): Promise<LocalStorageMigrationStatus> { export interface LocalStorageMigrationResult {
status: LocalStorageMigrationStatus;
error?: Error;
}
export async function migrateQueryHistoryFromLocalStorage(): Promise<LocalStorageMigrationResult> {
const richHistoryLocalStorage = new RichHistoryLocalStorage(); const richHistoryLocalStorage = new RichHistoryLocalStorage();
const richHistoryRemoteStorage = new RichHistoryRemoteStorage(); const richHistoryRemoteStorage = new RichHistoryRemoteStorage();
@ -145,14 +150,14 @@ export async function migrateQueryHistoryFromLocalStorage(): Promise<LocalStorag
to: 14, to: 14,
}); });
if (richHistory.length === 0) { if (richHistory.length === 0) {
return LocalStorageMigrationStatus.NotNeeded; return { status: LocalStorageMigrationStatus.NotNeeded };
} }
await richHistoryRemoteStorage.migrate(richHistory); await richHistoryRemoteStorage.migrate(richHistory);
dispatch(notifyApp(createSuccessNotification('Query history successfully migrated from local storage'))); dispatch(notifyApp(createSuccessNotification('Query history successfully migrated from local storage')));
return LocalStorageMigrationStatus.Successful; return { status: LocalStorageMigrationStatus.Successful };
} catch (error) { } catch (error) {
dispatch(notifyApp(createWarningNotification(`Query history migration failed. ${error.message}`))); dispatch(notifyApp(createWarningNotification(`Query history migration failed. ${error.message}`)));
return LocalStorageMigrationStatus.Failed; return { status: LocalStorageMigrationStatus.Failed, error };
} }
} }

View File

@ -1,7 +1,7 @@
import { AnyAction, createAction } from '@reduxjs/toolkit'; import { AnyAction, createAction } from '@reduxjs/toolkit';
import { DataQuery, HistoryItem } from '@grafana/data'; import { DataQuery, HistoryItem } from '@grafana/data';
import { config } from '@grafana/runtime'; import { config, logError } from '@grafana/runtime';
import { RICH_HISTORY_SETTING_KEYS } from 'app/core/history/richHistoryLocalStorageUtils'; import { RICH_HISTORY_SETTING_KEYS } from 'app/core/history/richHistoryLocalStorageUtils';
import store from 'app/core/store'; import store from 'app/core/store';
import { import {
@ -178,9 +178,10 @@ export const initRichHistory = (): ThunkResult<void> => {
// the migration attempt happens only once per session, and the user is informed about the failure // the migration attempt happens only once per session, and the user is informed about the failure
// in a way that can help with potential investigation. // in a way that can help with potential investigation.
if (config.queryHistoryEnabled && !queriesMigrated && !migrationFailedDuringThisSession) { if (config.queryHistoryEnabled && !queriesMigrated && !migrationFailedDuringThisSession) {
const migrationStatus = await migrateQueryHistoryFromLocalStorage(); const migrationResult = await migrateQueryHistoryFromLocalStorage();
if (migrationStatus === LocalStorageMigrationStatus.Failed) { if (migrationResult.status === LocalStorageMigrationStatus.Failed) {
dispatch(richHistoryMigrationFailedAction()); dispatch(richHistoryMigrationFailedAction());
logError(migrationResult.error!, { explore: { event: 'QueryHistoryMigrationFailed' } });
} else { } else {
store.set(RICH_HISTORY_SETTING_KEYS.migrated, true); store.set(RICH_HISTORY_SETTING_KEYS.migrated, true);
} }