1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 03:12:42 +02: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:
Mark Stacey 2023-08-18 16:32:28 -02:30
parent 016a1ef4e4
commit 0a3241e30d
4 changed files with 31 additions and 31 deletions

View File

@ -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) {

View File

@ -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;
} }

View File

@ -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;
}; };

View File

@ -36,14 +36,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');