Call exportState function before exporting metrics

Summary:
This diff fetches the additional data by calling the hook added in each plugins when the headless tries to export metrics directly out of the store rather than from the trace.

I added `fetchMetadata` function which will be called while exporting the state as well as exporting the metrics. We do not need to fetch metadata in the case when the metrics are exported from the given trace.

Reviewed By: passy

Differential Revision: D15560129

fbshipit-source-id: 9b14340e565ce17d1825bc2d32520d7b0c2b2219
This commit is contained in:
Pritesh Nandgaonkar
2019-06-03 15:03:25 -07:00
committed by Facebook Github Bot
parent 023135ad74
commit 95fae7d4e8
3 changed files with 76 additions and 41 deletions

View File

@@ -12,7 +12,7 @@ import yargs from 'yargs';
import dispatcher from '../src/dispatcher/index.js'; import dispatcher from '../src/dispatcher/index.js';
import {init as initLogger} from '../src/fb-stubs/Logger.js'; import {init as initLogger} from '../src/fb-stubs/Logger.js';
import reducers from '../src/reducers/index.js'; import reducers from '../src/reducers/index.js';
import {exportStore} from '../src/utils/exportData.js'; import {exportStore, pluginsClassMap} from '../src/utils/exportData.js';
import { import {
exportMetricsWithoutTrace, exportMetricsWithoutTrace,
exportMetricsFromTrace, exportMetricsFromTrace,
@@ -120,7 +120,10 @@ async function exitActions(
const {metrics, exit} = userArguments; const {metrics, exit} = userArguments;
if (shouldExportMetric(metrics) && metrics && metrics.length > 0) { if (shouldExportMetric(metrics) && metrics && metrics.length > 0) {
try { try {
const payload = await exportMetricsFromTrace(metrics, store.getState()); const payload = await exportMetricsFromTrace(
metrics,
pluginsClassMap(store.getState()),
);
originalConsole.log(payload); originalConsole.log(payload);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@@ -133,7 +136,7 @@ async function exitActions(
if (shouldExportMetric(metrics) && !metrics) { if (shouldExportMetric(metrics) && !metrics) {
const state = store.getState(); const state = store.getState();
const payload = await exportMetricsWithoutTrace( const payload = await exportMetricsWithoutTrace(
state, store,
state.pluginStates, state.pluginStates,
); );
originalConsole.log(payload); originalConsole.log(payload);

View File

@@ -25,6 +25,8 @@ import {readCurrentRevision} from './packageMetadata.js';
import {tryCatchReportPlatformFailures} from './metrics'; import {tryCatchReportPlatformFailures} from './metrics';
import {promisify} from 'util'; import {promisify} from 'util';
import promiseTimeout from './promiseTimeout'; import promiseTimeout from './promiseTimeout';
import type {State} from '../reducers';
export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace'; export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace';
export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace'; export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace';
@@ -46,6 +48,22 @@ export function processClients(
return clients.filter(client => client.query.device_id === serial); return clients.filter(client => client.query.device_id === serial);
} }
export function pluginsClassMap(
state: State,
): Map<string, Class<FlipperDevicePlugin<> | FlipperPlugin<>>> {
const pluginsMap: Map<
string,
Class<FlipperDevicePlugin<> | FlipperPlugin<>>,
> = new Map([]);
state.plugins.clientPlugins.forEach((val, key) => {
pluginsMap.set(key, val);
});
state.plugins.devicePlugins.forEach((val, key) => {
pluginsMap.set(key, val);
});
return pluginsMap;
}
export function processPluginStates( export function processPluginStates(
clients: Array<ClientExport>, clients: Array<ClientExport>,
serial: string, serial: string,
@@ -184,34 +202,17 @@ export const processStore = async (
return null; return null;
}; };
export async function getStoreExport( export async function fetchMetadata(
pluginStates: PluginStatesState,
pluginsMap: Map<string, Class<FlipperDevicePlugin<> | FlipperPlugin<>>>,
store: MiddlewareAPI, store: MiddlewareAPI,
): Promise<{exportData: ?ExportType, errorArray: Array<Error>}> { ): Promise<{pluginStates: PluginStatesState, errorArray: Array<Error>}> {
const state = store.getState();
const {clients} = state.connections;
const {pluginStates} = state;
const {plugins} = state;
const {selectedDevice} = store.getState().connections;
if (!selectedDevice) {
throw new Error('Please select a device before exporting data.');
}
const newPluginState = {...pluginStates}; const newPluginState = {...pluginStates};
// TODO: T39612653 Make Client mockable. Currently rsocket logic is tightly coupled.
// Not passing the entire state as currently Client is not mockable.
const pluginsMap: Map<
string,
Class<FlipperDevicePlugin<> | FlipperPlugin<>>,
> = new Map([]);
plugins.clientPlugins.forEach((val, key) => {
pluginsMap.set(key, val);
});
plugins.devicePlugins.forEach((val, key) => {
pluginsMap.set(key, val);
});
const errorArray: Array<Error> = []; const errorArray: Array<Error> = [];
const clients = store.getState().connections.clients;
const selectedDevice = store.getState().connections.selectedDevice;
for (let client of clients) { for (let client of clients) {
if (!client.id.includes(selectedDevice.serial)) { if (!selectedDevice || !client.id.includes(selectedDevice.serial)) {
continue; continue;
} }
for (let plugin of client.plugins) { for (let plugin of client.plugins) {
@@ -235,6 +236,37 @@ export async function getStoreExport(
} }
} }
} }
return {pluginStates: newPluginState, errorArray};
}
export async function getStoreExport(
store: MiddlewareAPI,
): Promise<{exportData: ?ExportType, errorArray: Array<Error>}> {
const state = store.getState();
const {clients} = state.connections;
const {pluginStates} = state;
const {plugins} = state;
const {selectedDevice} = store.getState().connections;
if (!selectedDevice) {
throw new Error('Please select a device before exporting data.');
}
// TODO: T39612653 Make Client mockable. Currently rsocket logic is tightly coupled.
// Not passing the entire state as currently Client is not mockable.
const pluginsMap: Map<
string,
Class<FlipperDevicePlugin<> | FlipperPlugin<>>,
> = new Map([]);
plugins.clientPlugins.forEach((val, key) => {
pluginsMap.set(key, val);
});
plugins.devicePlugins.forEach((val, key) => {
pluginsMap.set(key, val);
});
const metadata = await fetchMetadata(pluginStates, pluginsMap, store);
const {errorArray} = metadata;
const newPluginState = metadata.pluginStates;
const {activeNotifications} = store.getState().notifications; const {activeNotifications} = store.getState().notifications;
const {devicePlugins} = store.getState().plugins; const {devicePlugins} = store.getState().plugins;

View File

@@ -7,16 +7,17 @@
import type {FlipperPlugin, FlipperDevicePlugin} from 'flipper'; import type {FlipperPlugin, FlipperDevicePlugin} from 'flipper';
import {serialize} from './serialization'; import {serialize} from './serialization';
import type {State as PluginStatesState} from '../reducers/pluginStates'; import type {State as PluginStatesState} from '../reducers/pluginStates';
import type {State} from '../reducers/index.js'; import type {Store} from '../reducers';
import fs from 'fs'; import fs from 'fs';
import type {ExportType} from './exportData'; import type {ExportType} from './exportData';
import {fetchMetadata, pluginsClassMap} from './exportData';
import {deserializeObject} from './serialization'; import {deserializeObject} from './serialization';
export type MetricType = {[metricName: string]: number}; export type MetricType = {[metricName: string]: number};
type MetricPluginType = {[pluginID: string]: MetricType}; type MetricPluginType = {[pluginID: string]: MetricType};
export type ExportMetricType = {[clientID: string]: MetricPluginType}; export type ExportMetricType = {[clientID: string]: MetricPluginType};
export async function exportMetrics( async function exportMetrics(
pluginStates: PluginStatesState, pluginStates: PluginStatesState,
pluginsMap: Map<string, Class<FlipperDevicePlugin<> | FlipperPlugin<>>>, pluginsMap: Map<string, Class<FlipperDevicePlugin<> | FlipperPlugin<>>>,
): Promise<string> { ): Promise<string> {
@@ -45,21 +46,21 @@ export async function exportMetrics(
} }
export async function exportMetricsWithoutTrace( export async function exportMetricsWithoutTrace(
state: State, store: Store,
pluginStates: PluginStatesState, pluginStates: PluginStatesState,
): Promise<?string> { ): Promise<?string> {
const pluginsMap: Map< const pluginsMap: Map<
string, string,
Class<FlipperDevicePlugin<> | FlipperPlugin<>>, Class<FlipperDevicePlugin<> | FlipperPlugin<>>,
> = new Map([]); > = pluginsClassMap(store.getState());
state.plugins.clientPlugins.forEach((val, key) => { const metadata = await fetchMetadata(pluginStates, pluginsMap, store);
pluginsMap.set(key, val); const newPluginStates = metadata.pluginStates;
}); const {errorArray} = metadata;
state.plugins.devicePlugins.forEach((val, key) => { if (errorArray.length > 0) {
pluginsMap.set(key, val); console.error(errorArray);
}); }
const metrics = await exportMetrics(pluginStates, pluginsMap); const metrics = await exportMetrics(newPluginStates, pluginsMap);
return metrics; return metrics;
} }
@@ -74,7 +75,7 @@ function parseJSON(str: string): ?any {
export async function exportMetricsFromTrace( export async function exportMetricsFromTrace(
trace: string, trace: string,
state: State, pluginsMap: Map<string, Class<FlipperDevicePlugin<> | FlipperPlugin<>>>,
): Promise<?string> { ): Promise<?string> {
const data = fs.readFileSync(trace, 'utf8'); const data = fs.readFileSync(trace, 'utf8');
const parsedJSONData = parseJSON(data); const parsedJSONData = parseJSON(data);
@@ -102,6 +103,5 @@ export async function exportMetricsFromTrace(
), ),
); );
} }
const metrics = await exportMetricsWithoutTrace(state, pluginStates); return await exportMetrics(pluginStates, pluginsMap);
return metrics;
} }