mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-25 11:28:51 +01:00
Capture app and migration version (#20458)
* Add AppMetadataController so current and previous application and migration version can be captured in sentry * Add currentAppVersion, previousAppVersion, previousMigrationVersion, currentMigrationVersion to SENTRY_OBJECT * Update app/scripts/controllers/app-metadata.ts Co-authored-by: Mark Stacey <markjstacey@gmail.com> * Update app/scripts/controllers/app-metadata.ts Co-authored-by: Mark Stacey <markjstacey@gmail.com> * Update app/scripts/controllers/app-metadata.ts Co-authored-by: Mark Stacey <markjstacey@gmail.com> * Fix types * Add tests for app-metadata.test.ts * Lint fixes * Modify loadStateFromPersistence to return the whole versionData object, so that the migration version can be passed to the metamask-controller on instantiation * Remove reference to implementation details in test descriptions in app/scripts/controllers/app-metadata.test.ts * Reset all mocks afterEach in AppMetadataController * Refactor AppMetadataController to be passed version instead of calling platform.version directly (for ease of unit testing the MetaMask Controller) * Make maybeUpdateAppVersion and maybeUpdateMigrationVersion private, and remove unit tests of those specific functions --------- Co-authored-by: Mark Stacey <markjstacey@gmail.com>
This commit is contained in:
parent
594dde58b1
commit
789122d587
@ -264,7 +264,8 @@ browser.runtime.onConnectExternal.addListener(async (...args) => {
|
||||
*/
|
||||
async function initialize() {
|
||||
try {
|
||||
const initState = await loadStateFromPersistence();
|
||||
const initData = await loadStateFromPersistence();
|
||||
const initState = initData.data;
|
||||
const initLangCode = await getFirstPreferredLangCode();
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
@ -287,6 +288,7 @@ async function initialize() {
|
||||
initLangCode,
|
||||
{},
|
||||
isFirstMetaMaskControllerSetup,
|
||||
initData.meta,
|
||||
);
|
||||
if (!isManifestV3) {
|
||||
await loadPhishingWarningPage();
|
||||
@ -417,7 +419,7 @@ export async function loadStateFromPersistence() {
|
||||
localStore.set(versionedData.data);
|
||||
|
||||
// return just the data
|
||||
return versionedData.data;
|
||||
return versionedData;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -430,12 +432,14 @@ export async function loadStateFromPersistence() {
|
||||
* @param {string} initLangCode - The region code for the language preferred by the current user.
|
||||
* @param {object} overrides - object with callbacks that are allowed to override the setup controller logic (usefull for desktop app)
|
||||
* @param isFirstMetaMaskControllerSetup
|
||||
* @param {object} stateMetadata - Metadata about the initial state and migrations, including the most recent migration version
|
||||
*/
|
||||
export function setupController(
|
||||
initState,
|
||||
initLangCode,
|
||||
overrides,
|
||||
isFirstMetaMaskControllerSetup,
|
||||
stateMetadata,
|
||||
) {
|
||||
//
|
||||
// MetaMask Controller
|
||||
@ -462,6 +466,7 @@ export function setupController(
|
||||
localStore,
|
||||
overrides,
|
||||
isFirstMetaMaskControllerSetup,
|
||||
currentMigrationVersion: stateMetadata.version,
|
||||
});
|
||||
|
||||
setupEnsIpfsResolver({
|
||||
|
104
app/scripts/controllers/app-metadata.test.ts
Normal file
104
app/scripts/controllers/app-metadata.test.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import assert from 'assert';
|
||||
import AppMetadataController from './app-metadata';
|
||||
|
||||
const EXPECTED_DEFAULT_STATE = {
|
||||
currentAppVersion: '',
|
||||
previousAppVersion: '',
|
||||
previousMigrationVersion: 0,
|
||||
currentMigrationVersion: 0,
|
||||
};
|
||||
|
||||
describe('AppMetadataController', () => {
|
||||
describe('constructor', () => {
|
||||
it('accepts initial state and does not modify it if currentMigrationVersion and platform.getVersion() match respective values in state', async () => {
|
||||
const initState = {
|
||||
currentAppVersion: '1',
|
||||
previousAppVersion: '1',
|
||||
previousMigrationVersion: 1,
|
||||
currentMigrationVersion: 1,
|
||||
};
|
||||
const appMetadataController = new AppMetadataController({
|
||||
state: initState,
|
||||
currentMigrationVersion: 1,
|
||||
currentAppVersion: '1',
|
||||
});
|
||||
assert.deepStrictEqual(appMetadataController.store.getState(), initState);
|
||||
});
|
||||
|
||||
it('sets default state and does not modify it', async () => {
|
||||
const appMetadataController = new AppMetadataController({
|
||||
state: {},
|
||||
});
|
||||
assert.deepStrictEqual(
|
||||
appMetadataController.store.getState(),
|
||||
EXPECTED_DEFAULT_STATE,
|
||||
);
|
||||
});
|
||||
|
||||
it('sets default state and does not modify it if options version parameters match respective default values', async () => {
|
||||
const appMetadataController = new AppMetadataController({
|
||||
state: {},
|
||||
currentMigrationVersion: 0,
|
||||
currentAppVersion: '',
|
||||
});
|
||||
assert.deepStrictEqual(
|
||||
appMetadataController.store.getState(),
|
||||
EXPECTED_DEFAULT_STATE,
|
||||
);
|
||||
});
|
||||
|
||||
it('updates the currentAppVersion state property if options.currentAppVersion does not match the default value', async () => {
|
||||
const appMetadataController = new AppMetadataController({
|
||||
state: {},
|
||||
currentMigrationVersion: 0,
|
||||
currentAppVersion: '1',
|
||||
});
|
||||
assert.deepStrictEqual(appMetadataController.store.getState(), {
|
||||
...EXPECTED_DEFAULT_STATE,
|
||||
currentAppVersion: '1',
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the currentAppVersion and previousAppVersion state properties if options.currentAppVersion, currentAppVersion and previousAppVersion are all different', async () => {
|
||||
const appMetadataController = new AppMetadataController({
|
||||
state: {
|
||||
currentAppVersion: '2',
|
||||
previousAppVersion: '1',
|
||||
},
|
||||
currentAppVersion: '3',
|
||||
currentMigrationVersion: 0,
|
||||
});
|
||||
assert.deepStrictEqual(appMetadataController.store.getState(), {
|
||||
...EXPECTED_DEFAULT_STATE,
|
||||
currentAppVersion: '3',
|
||||
previousAppVersion: '2',
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the currentMigrationVersion state property if the currentMigrationVersion param does not match the default value', async () => {
|
||||
const appMetadataController = new AppMetadataController({
|
||||
state: {},
|
||||
currentMigrationVersion: 1,
|
||||
});
|
||||
assert.deepStrictEqual(appMetadataController.store.getState(), {
|
||||
...EXPECTED_DEFAULT_STATE,
|
||||
currentMigrationVersion: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the currentMigrationVersion and previousMigrationVersion state properties if the currentMigrationVersion param, the currentMigrationVersion state property and the previousMigrationVersion state property are all different', async () => {
|
||||
const appMetadataController = new AppMetadataController({
|
||||
state: {
|
||||
currentMigrationVersion: 2,
|
||||
previousMigrationVersion: 1,
|
||||
},
|
||||
currentMigrationVersion: 3,
|
||||
});
|
||||
assert.deepStrictEqual(appMetadataController.store.getState(), {
|
||||
...EXPECTED_DEFAULT_STATE,
|
||||
currentMigrationVersion: 3,
|
||||
previousMigrationVersion: 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
99
app/scripts/controllers/app-metadata.ts
Normal file
99
app/scripts/controllers/app-metadata.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import EventEmitter from 'events';
|
||||
import { ObservableStore } from '@metamask/obs-store';
|
||||
|
||||
/**
|
||||
* The state of the AppMetadataController
|
||||
*/
|
||||
export type AppMetadataControllerState = {
|
||||
currentAppVersion: string;
|
||||
previousAppVersion: string;
|
||||
previousMigrationVersion: number;
|
||||
currentMigrationVersion: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* The options that NetworkController takes.
|
||||
*/
|
||||
export type AppMetadataControllerOptions = {
|
||||
currentMigrationVersion?: number;
|
||||
currentAppVersion?: string;
|
||||
state?: Partial<AppMetadataControllerState>;
|
||||
};
|
||||
|
||||
const defaultState: AppMetadataControllerState = {
|
||||
currentAppVersion: '',
|
||||
previousAppVersion: '',
|
||||
previousMigrationVersion: 0,
|
||||
currentMigrationVersion: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* The AppMetadata controller stores metadata about the current extension instance,
|
||||
* including the currently and previously installed versions, and the most recently
|
||||
* run migration.
|
||||
*
|
||||
*/
|
||||
export default class AppMetadataController extends EventEmitter {
|
||||
/**
|
||||
* Observable store containing controller data.
|
||||
*/
|
||||
store: ObservableStore<AppMetadataControllerState>;
|
||||
|
||||
/**
|
||||
* Constructs a AppMetadata controller.
|
||||
*
|
||||
* @param options - the controller options
|
||||
* @param options.state - Initial controller state.
|
||||
* @param options.currentMigrationVersion
|
||||
* @param options.currentAppVersion
|
||||
*/
|
||||
constructor({
|
||||
currentAppVersion = '',
|
||||
currentMigrationVersion = 0,
|
||||
state = {},
|
||||
}: AppMetadataControllerOptions) {
|
||||
super();
|
||||
|
||||
this.store = new ObservableStore({
|
||||
...defaultState,
|
||||
...state,
|
||||
});
|
||||
|
||||
this.#maybeUpdateAppVersion(currentAppVersion);
|
||||
|
||||
this.#maybeUpdateMigrationVersion(currentMigrationVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the currentAppVersion in state, and sets the previousAppVersion to the old currentAppVersion.
|
||||
*
|
||||
* @param maybeNewAppVersion
|
||||
*/
|
||||
#maybeUpdateAppVersion(maybeNewAppVersion: string): void {
|
||||
const oldCurrentAppVersion = this.store.getState().currentAppVersion;
|
||||
|
||||
if (maybeNewAppVersion !== oldCurrentAppVersion) {
|
||||
this.store.updateState({
|
||||
currentAppVersion: maybeNewAppVersion,
|
||||
previousAppVersion: oldCurrentAppVersion,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the migrationVersion in state.
|
||||
*
|
||||
* @param maybeNewMigrationVersion
|
||||
*/
|
||||
#maybeUpdateMigrationVersion(maybeNewMigrationVersion: number): void {
|
||||
const oldCurrentMigrationVersion =
|
||||
this.store.getState().currentMigrationVersion;
|
||||
|
||||
if (maybeNewMigrationVersion !== oldCurrentMigrationVersion) {
|
||||
this.store.updateState({
|
||||
previousMigrationVersion: oldCurrentMigrationVersion,
|
||||
currentMigrationVersion: maybeNewMigrationVersion,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -35,9 +35,11 @@ export const SENTRY_STATE = {
|
||||
connectedStatusPopoverHasBeenShown: true,
|
||||
conversionDate: true,
|
||||
conversionRate: true,
|
||||
currentAppVersion: true,
|
||||
currentBlockGasLimit: true,
|
||||
currentCurrency: true,
|
||||
currentLocale: true,
|
||||
currentMigrationVersion: true,
|
||||
customNonceValue: true,
|
||||
defaultHomeActiveTabName: true,
|
||||
desktopEnabled: true,
|
||||
@ -56,6 +58,8 @@ export const SENTRY_STATE = {
|
||||
nextNonce: true,
|
||||
participateInMetaMetrics: true,
|
||||
preferences: true,
|
||||
previousAppVersion: true,
|
||||
previousMigrationVersion: true,
|
||||
providerConfig: {
|
||||
nickname: true,
|
||||
ticker: true,
|
||||
|
@ -192,6 +192,7 @@ import createMetaRPCHandler from './lib/createMetaRPCHandler';
|
||||
import { previousValueComparator } from './lib/util';
|
||||
import createMetamaskMiddleware from './lib/createMetamaskMiddleware';
|
||||
import EncryptionPublicKeyController from './controllers/encryption-public-key';
|
||||
import AppMetadataController from './controllers/app-metadata';
|
||||
|
||||
import {
|
||||
CaveatMutatorFactories,
|
||||
@ -258,6 +259,8 @@ export default class MetamaskController extends EventEmitter {
|
||||
// instance of a class that wraps the extension's storage local API.
|
||||
this.localStoreApiWrapper = opts.localStore;
|
||||
|
||||
this.currentMigrationVersion = opts.currentMigrationVersion;
|
||||
|
||||
// observable state store
|
||||
this.store = new ComposableObservableStore({
|
||||
state: initState,
|
||||
@ -278,6 +281,12 @@ export default class MetamaskController extends EventEmitter {
|
||||
}
|
||||
});
|
||||
|
||||
this.appMetadataController = new AppMetadataController({
|
||||
state: initState.AppMetadataController,
|
||||
currentMigrationVersion: this.currentMigrationVersion,
|
||||
currentAppVersion: version,
|
||||
});
|
||||
|
||||
// next, we will initialize the controllers
|
||||
// controller initialization order matters
|
||||
|
||||
@ -1506,6 +1515,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
|
||||
this.store.updateStructure({
|
||||
AppStateController: this.appStateController.store,
|
||||
AppMetadataController: this.appMetadataController.store,
|
||||
TransactionController: this.txController.store,
|
||||
KeyringController: this.keyringController.store,
|
||||
PreferencesController: this.preferencesController.store,
|
||||
@ -1550,6 +1560,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
this.memStore = new ComposableObservableStore({
|
||||
config: {
|
||||
AppStateController: this.appStateController.store,
|
||||
AppMetadataController: this.appMetadataController.store,
|
||||
NetworkController: this.networkController,
|
||||
CachedBalancesController: this.cachedBalancesController.store,
|
||||
KeyringController: this.keyringController.memStore,
|
||||
|
Loading…
Reference in New Issue
Block a user