From e5294d34f0d0f9dc981e2c03ab8d3256e10743e0 Mon Sep 17 00:00:00 2001 From: Pritesh Nandgaonkar Date: Mon, 17 Jun 2019 06:14:49 -0700 Subject: [PATCH] Plugin name list Summary: Adds an argument to the headless Flipper to print the list of available plugins. Added `--list-plugins`. Currently the startFlipper function is not scalable enough to add new arguments and its implementation. I am planning to tackle this with the list of Actions which will be closure. So adding a new argument with its implementation will be just appending a closure at the correct location in an array. Will work on that in the later diff stacked on the current one. Reviewed By: jknoxville Differential Revision: D15789778 fbshipit-source-id: 91ba472617d593c3490bb932590a06d83597cba7 --- headless/index.js | 14 +++++- src/utils/__tests__/pluginUtils.node.js | 62 +++++++++++++++++++++++++ src/utils/exportData.js | 6 +-- src/utils/exportMetrics.js | 2 +- src/utils/pluginUtils.js | 26 +++++++++++ 5 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 src/utils/__tests__/pluginUtils.node.js diff --git a/headless/index.js b/headless/index.js index f484de001..612430227 100644 --- a/headless/index.js +++ b/headless/index.js @@ -21,6 +21,8 @@ import {listDevices} from '../src/utils/listDevices'; // $FlowFixMe this file exist, trust me, flow! import setup from '../static/setup.js'; import type {Store} from '../src/reducers'; +import {getActivePluginNames} from '../src/utils/pluginUtils.js'; +import {serialize} from '../src/utils/serialization'; type UserArguments = {| securePort: string, @@ -31,6 +33,7 @@ type UserArguments = {| metrics: string, listDevices: boolean, device: string, + listPlugins: boolean, |}; yargs @@ -76,6 +79,11 @@ yargs describe: 'Will print the list of devices in the terminal', type: 'boolean', }); + yargs.option('list-plugins', { + default: false, + describe: 'Will print the list of supported plugins in the terminal', + type: 'boolean', + }); yargs.option('device', { default: undefined, describe: @@ -122,12 +130,16 @@ async function exitActions( originalConsole: typeof global.console, store: Store, ): Promise { + if (userArguments.listPlugins) { + outputAndExit(serialize(getActivePluginNames(store.getState().plugins))); + } + const {metrics, exit} = userArguments; if (shouldExportMetric(metrics) && metrics && metrics.length > 0) { try { const payload = await exportMetricsFromTrace( metrics, - pluginsClassMap(store.getState()), + pluginsClassMap(store.getState().plugins), ); outputAndExit(payload.toString()); } catch (error) { diff --git a/src/utils/__tests__/pluginUtils.node.js b/src/utils/__tests__/pluginUtils.node.js new file mode 100644 index 000000000..540756a49 --- /dev/null +++ b/src/utils/__tests__/pluginUtils.node.js @@ -0,0 +1,62 @@ +/** + * Copyright 2018-present Facebook. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * @format + */ + +import {getActivePluginNames} from '../pluginUtils'; +import type {State as PluginsState} from '../../reducers/plugins.js'; +import type {PluginDefinition} from '../../dispatcher/plugins'; + +function mockPluginState( + gatekeepedPlugins: Array, + disabledPlugins: Array, + failedPlugins: Array<[PluginDefinition, string]>, +): PluginsState { + return { + devicePlugins: new Map([ + //$FlowFixMe: Class instance won't be used in the test + ['DevicePlugin1', undefined], + //$FlowFixMe: Class instance won't be used in the test + ['DevicePlugin2', undefined], + ]), + clientPlugins: new Map([ + //$FlowFixMe: Class instance won't be used in the test + ['ClientPlugin1', undefined], + //$FlowFixMe: Class instance won't be used in the test + ['ClientPlugin2', undefined], + ]), + gatekeepedPlugins, + disabledPlugins, + failedPlugins, + }; +} + +function mockPluginDefinition(name: string): PluginDefinition { + return { + name, + out: 'out', + }; +} + +test('getActivePluginNames with the plugins getting excluded', () => { + let state = mockPluginState( + [mockPluginDefinition('DevicePlugin1')], + [mockPluginDefinition('ClientPlugin1')], + [[mockPluginDefinition('DevicePlugin2'), 'DevicePlugin2']], + ); + let list = getActivePluginNames(state); + expect(list).toEqual(['ClientPlugin2']); +}); + +test('getActivePluginNames with the no plugins getting excluded', () => { + let state = mockPluginState([], [], []); + let list = getActivePluginNames(state); + expect(list).toEqual([ + 'ClientPlugin1', + 'ClientPlugin2', + 'DevicePlugin1', + 'DevicePlugin2', + ]); +}); diff --git a/src/utils/exportData.js b/src/utils/exportData.js index 7cd589bf6..c743d5c79 100644 --- a/src/utils/exportData.js +++ b/src/utils/exportData.js @@ -49,16 +49,16 @@ export function processClients( } export function pluginsClassMap( - state: State, + plugins: PluginStates, ): Map | FlipperPlugin<>>> { const pluginsMap: Map< string, Class | FlipperPlugin<>>, > = new Map([]); - state.plugins.clientPlugins.forEach((val, key) => { + plugins.clientPlugins.forEach((val, key) => { pluginsMap.set(key, val); }); - state.plugins.devicePlugins.forEach((val, key) => { + plugins.devicePlugins.forEach((val, key) => { pluginsMap.set(key, val); }); return pluginsMap; diff --git a/src/utils/exportMetrics.js b/src/utils/exportMetrics.js index 7ae60e333..50c787987 100644 --- a/src/utils/exportMetrics.js +++ b/src/utils/exportMetrics.js @@ -52,7 +52,7 @@ export async function exportMetricsWithoutTrace( const pluginsMap: Map< string, Class | FlipperPlugin<>>, - > = pluginsClassMap(store.getState()); + > = pluginsClassMap(store.getState().plugins); const metadata = await fetchMetadata(pluginStates, pluginsMap, store); const newPluginStates = metadata.pluginStates; const {errorArray} = metadata; diff --git a/src/utils/pluginUtils.js b/src/utils/pluginUtils.js index c6186110e..6962a55fd 100644 --- a/src/utils/pluginUtils.js +++ b/src/utils/pluginUtils.js @@ -7,6 +7,9 @@ import type BaseDevice from '../devices/BaseDevice.js'; import {FlipperDevicePlugin, FlipperPlugin} from '../plugin.js'; import type {State as PluginStatesState} from '../reducers/pluginStates.js'; +import {pluginsClassMap} from './exportData.js'; +import type {State as PluginsState} from '../reducers/plugins.js'; +import type {PluginDefinition} from '../dispatcher/plugins'; export function getPluginKey( selectedApp: ?string, @@ -41,3 +44,26 @@ export function getPersistedState( }; return persistedState; } + +export function getActivePluginNames(plugins: PluginsState): Array { + let pluginsMap: Map< + string, + Class | FlipperPlugin<>>, + > = pluginsClassMap(plugins); + + let arr: Array = plugins.disabledPlugins.concat( + plugins.gatekeepedPlugins, + ); + arr.forEach((plugin: PluginDefinition) => { + if (pluginsMap.has(plugin.name)) { + pluginsMap.delete(plugin.name); + } + }); + + plugins.failedPlugins.forEach((plugin: [PluginDefinition, string]) => { + if (plugin[0] && plugin[0].name && pluginsMap.has(plugin[0].name)) { + pluginsMap.delete(plugin[0].name); + } + }); + return [...pluginsMap.keys()]; +}