mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-24 19:10:22 +01:00
Fix pre-initialization UI error state capture (#20529)
In the case where an error is thrown in the UI before initialization has finished, we aren't capturing the application state correctly for Sentry errors. We had a test case for this, but the test case was broken due to a mistake in how the `network-store` was setup (it was not matching the behavior of the real `local-tstore` module). The pre-initialization state capture logic was updated to rely solely upon the `localStore` instance used by Sentry to determine whether the user had opted-in to metrics or not. This simplifies the logic a great deal, removing the need for the `getMostRecentPersistedState` state hook. It also ensures that state is captured corretly pre- initialization in both the background and UI.
This commit is contained in:
parent
3d9457e517
commit
9cfa9ba6b0
@ -71,12 +71,6 @@ import DesktopManager from '@metamask/desktop/dist/desktop-manager';
|
|||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
/* eslint-enable import/order */
|
/* eslint-enable import/order */
|
||||||
|
|
||||||
// Setup global hook for improved Sentry state snapshots during initialization
|
|
||||||
const inTest = process.env.IN_TEST;
|
|
||||||
const localStore = inTest ? new ReadOnlyNetworkStore() : new LocalStore();
|
|
||||||
global.stateHooks.getMostRecentPersistedState = () =>
|
|
||||||
localStore.mostRecentRetrievedState;
|
|
||||||
|
|
||||||
const { sentry } = global;
|
const { sentry } = global;
|
||||||
const firstTimeState = { ...rawFirstTimeState };
|
const firstTimeState = { ...rawFirstTimeState };
|
||||||
|
|
||||||
@ -100,6 +94,9 @@ const openMetamaskTabsIDs = {};
|
|||||||
const requestAccountTabIds = {};
|
const requestAccountTabIds = {};
|
||||||
let controller;
|
let controller;
|
||||||
|
|
||||||
|
// state persistence
|
||||||
|
const inTest = process.env.IN_TEST;
|
||||||
|
const localStore = inTest ? new ReadOnlyNetworkStore() : new LocalStore();
|
||||||
let versionedData;
|
let versionedData;
|
||||||
|
|
||||||
if (inTest || process.env.METAMASK_DEBUG) {
|
if (inTest || process.env.METAMASK_DEBUG) {
|
||||||
|
@ -31,7 +31,6 @@ export default class ReadOnlyNetworkStore {
|
|||||||
const response = await fetchWithTimeout(FIXTURE_SERVER_URL);
|
const response = await fetchWithTimeout(FIXTURE_SERVER_URL);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
this._state = await response.json();
|
this._state = await response.json();
|
||||||
this.mostRecentRetrievedState = this._state;
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.debug(`Error loading network state: '${error.message}'`);
|
log.debug(`Error loading network state: '${error.message}'`);
|
||||||
@ -49,6 +48,11 @@ export default class ReadOnlyNetworkStore {
|
|||||||
if (!this._initialized) {
|
if (!this._initialized) {
|
||||||
await this._initializing;
|
await this._initializing;
|
||||||
}
|
}
|
||||||
|
// Delay setting this until after the first read, to match the
|
||||||
|
// behavior of the local store.
|
||||||
|
if (!this.mostRecentRetrievedState) {
|
||||||
|
this.mostRecentRetrievedState = this._state;
|
||||||
|
}
|
||||||
return this._state;
|
return this._state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,9 @@ import ReadOnlyNetworkStore from './network-store';
|
|||||||
import { SENTRY_BACKGROUND_STATE } from './setupSentry';
|
import { SENTRY_BACKGROUND_STATE } from './setupSentry';
|
||||||
|
|
||||||
const platform = new ExtensionPlatform();
|
const platform = new ExtensionPlatform();
|
||||||
const localStore = process.env.IN_TEST
|
|
||||||
|
// This instance of `localStore` is used by Sentry to get the persisted state
|
||||||
|
const sentryLocalStore = process.env.IN_TEST
|
||||||
? new ReadOnlyNetworkStore()
|
? new ReadOnlyNetworkStore()
|
||||||
: new LocalStore();
|
: new LocalStore();
|
||||||
|
|
||||||
@ -15,7 +17,7 @@ const localStore = process.env.IN_TEST
|
|||||||
* @returns The persisted wallet state.
|
* @returns The persisted wallet state.
|
||||||
*/
|
*/
|
||||||
globalThis.stateHooks.getPersistedState = async function () {
|
globalThis.stateHooks.getPersistedState = async function () {
|
||||||
return await localStore.get();
|
return await sentryLocalStore.get();
|
||||||
};
|
};
|
||||||
|
|
||||||
const persistedStateMask = {
|
const persistedStateMask = {
|
||||||
@ -37,25 +39,30 @@ globalThis.stateHooks.getSentryState = function () {
|
|||||||
browser: window.navigator.userAgent,
|
browser: window.navigator.userAgent,
|
||||||
version: platform.getVersion(),
|
version: platform.getVersion(),
|
||||||
};
|
};
|
||||||
|
// If `getSentryAppState` is set, it implies that initialization has completed
|
||||||
if (globalThis.stateHooks.getSentryAppState) {
|
if (globalThis.stateHooks.getSentryAppState) {
|
||||||
return {
|
return {
|
||||||
...sentryState,
|
...sentryState,
|
||||||
state: globalThis.stateHooks.getSentryAppState(),
|
state: globalThis.stateHooks.getSentryAppState(),
|
||||||
};
|
};
|
||||||
} else if (globalThis.stateHooks.getMostRecentPersistedState) {
|
} else if (
|
||||||
const persistedState = globalThis.stateHooks.getMostRecentPersistedState();
|
// This is truthy if Sentry has retrieved state at least once already. This
|
||||||
if (persistedState) {
|
// should always be true because Sentry calls `getPersistedState` during
|
||||||
return {
|
// error processing (before this function is called) if `getSentryAppState`
|
||||||
...sentryState,
|
// hasn't been set yet.
|
||||||
persistedState: maskObject(
|
sentryLocalStore.mostRecentRetrievedState
|
||||||
// `getMostRecentPersistedState` is used here instead of
|
) {
|
||||||
// `getPersistedState` to avoid making this an asynchronous function.
|
return {
|
||||||
globalThis.stateHooks.getMostRecentPersistedState(),
|
...sentryState,
|
||||||
persistedStateMask,
|
persistedState: maskObject(
|
||||||
),
|
sentryLocalStore.mostRecentRetrievedState,
|
||||||
};
|
persistedStateMask,
|
||||||
}
|
),
|
||||||
return sentryState;
|
};
|
||||||
}
|
}
|
||||||
|
// This branch means that local storage has not yet been read, so we have
|
||||||
|
// no choice but to omit the application state.
|
||||||
|
// This should be unreachable, unless an error was encountered during error
|
||||||
|
// processing.
|
||||||
return sentryState;
|
return sentryState;
|
||||||
};
|
};
|
||||||
|
@ -33,14 +33,6 @@ import ExtensionPlatform from './platforms/extension';
|
|||||||
import { setupMultiplex } from './lib/stream-utils';
|
import { setupMultiplex } from './lib/stream-utils';
|
||||||
import { getEnvironmentType, getPlatform } from './lib/util';
|
import { getEnvironmentType, getPlatform } from './lib/util';
|
||||||
import metaRPCClientFactory from './lib/metaRPCClientFactory';
|
import metaRPCClientFactory from './lib/metaRPCClientFactory';
|
||||||
import LocalStore from './lib/local-store';
|
|
||||||
import ReadOnlyNetworkStore from './lib/network-store';
|
|
||||||
|
|
||||||
// Setup global hook for improved Sentry state snapshots during initialization
|
|
||||||
const inTest = process.env.IN_TEST;
|
|
||||||
const localStore = inTest ? new ReadOnlyNetworkStore() : new LocalStore();
|
|
||||||
global.stateHooks.getMostRecentPersistedState = () =>
|
|
||||||
localStore.mostRecentRetrievedState;
|
|
||||||
|
|
||||||
const container = document.getElementById('app-content');
|
const container = document.getElementById('app-content');
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user