Make Client initialize server add-ons

Reviewed By: mweststrate

Differential Revision: D34044353

fbshipit-source-id: 99bcb1559787b2a904bdd796233666a7a4783ea4
This commit is contained in:
Andrey Goncharov
2022-02-28 03:50:34 -08:00
committed by Facebook GitHub Bot
parent a60865f0be
commit 9113006851
12 changed files with 144 additions and 4 deletions

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import {FlipperServerCommands} from './server-types';
export interface ServerAddOnControls {
start: FlipperServerCommands['plugins-server-add-on-start'];
stop: FlipperServerCommands['plugins-server-add-on-stop'];
}

View File

@@ -17,6 +17,7 @@ export {
NoopLogger, NoopLogger,
} from './utils/Logger'; } from './utils/Logger';
export * from './server-types'; export * from './server-types';
export * from './ServerAddOn';
export {sleep} from './utils/sleep'; export {sleep} from './utils/sleep';
export {timeout} from './utils/timeout'; export {timeout} from './utils/timeout';
export {isTest} from './utils/isTest'; export {isTest} from './utils/isTest';

View File

@@ -11,7 +11,13 @@ import {SandyPluginDefinition} from './SandyPluginDefinition';
import {BasePluginInstance, BasePluginClient} from './PluginBase'; import {BasePluginInstance, BasePluginClient} from './PluginBase';
import {FlipperLib} from './FlipperLib'; import {FlipperLib} from './FlipperLib';
import {Atom, ReadOnlyAtom} from '../state/atom'; import {Atom, ReadOnlyAtom} from '../state/atom';
import {DeviceOS, DeviceType, DeviceLogEntry, CrashLog} from 'flipper-common'; import {
DeviceOS,
DeviceType,
DeviceLogEntry,
CrashLog,
ServerAddOnControls,
} from 'flipper-common';
export type DeviceLogListener = (entry: DeviceLogEntry) => void; export type DeviceLogListener = (entry: DeviceLogEntry) => void;
export type CrashLogListener = (crash: CrashLog) => void; export type CrashLogListener = (crash: CrashLog) => void;
@@ -59,6 +65,7 @@ export class SandyDevicePluginInstance extends BasePluginInstance {
readonly client: DevicePluginClient; readonly client: DevicePluginClient;
constructor( constructor(
private readonly serverAddOnControls: ServerAddOnControls,
flipperLib: FlipperLib, flipperLib: FlipperLib,
definition: SandyPluginDefinition, definition: SandyPluginDefinition,
device: Device, device: Device,
@@ -79,9 +86,33 @@ export class SandyDevicePluginInstance extends BasePluginInstance {
this.initializePlugin(() => this.initializePlugin(() =>
definition.asDevicePluginModule().devicePlugin(this.client), definition.asDevicePluginModule().devicePlugin(this.client),
); );
this.startServerAddOn();
} }
toJSON() { toJSON() {
return '[SandyDevicePluginInstance]'; return '[SandyDevicePluginInstance]';
} }
destroy() {
this.stopServerAddOn();
super.destroy();
}
private startServerAddOn() {
const {serverAddOn, name} = this.definition.details;
if (serverAddOn) {
this.serverAddOnControls.start(name).catch((e) => {
console.warn('Failed to start a server add on', name, e);
});
}
}
private stopServerAddOn() {
const {serverAddOn, name} = this.definition.details;
if (serverAddOn) {
this.serverAddOnControls.stop(name).catch((e) => {
console.warn('Failed to start a server add on', name, e);
});
}
}
} }

View File

@@ -13,6 +13,7 @@ import {FlipperLib} from './FlipperLib';
import {Device} from './DevicePlugin'; import {Device} from './DevicePlugin';
import {batched} from '../state/batch'; import {batched} from '../state/batch';
import {Atom, createState, ReadOnlyAtom} from '../state/atom'; import {Atom, createState, ReadOnlyAtom} from '../state/atom';
import {ServerAddOnControls} from 'flipper-common';
type EventsContract = Record<string, any>; type EventsContract = Record<string, any>;
type MethodsContract = Record<string, (params: any) => Promise<any>>; type MethodsContract = Record<string, (params: any) => Promise<any>>;
@@ -141,6 +142,7 @@ export class SandyPluginInstance extends BasePluginInstance {
readonly connected = createState(false); readonly connected = createState(false);
constructor( constructor(
private readonly serverAddOnControls: ServerAddOnControls,
flipperLib: FlipperLib, flipperLib: FlipperLib,
definition: SandyPluginDefinition, definition: SandyPluginDefinition,
realClient: RealFlipperClient, realClient: RealFlipperClient,
@@ -229,6 +231,13 @@ export class SandyPluginInstance extends BasePluginInstance {
connect() { connect() {
this.assertNotDestroyed(); this.assertNotDestroyed();
if (!this.connected.get()) { if (!this.connected.get()) {
const {serverAddOn, name} = this.definition.details;
if (serverAddOn) {
this.serverAddOnControls.start(name).catch((e) => {
console.warn('Failed to start a server add on', name, e);
});
}
this.connected.set(true); this.connected.set(true);
this.events.emit('connect'); this.events.emit('connect');
} }
@@ -237,6 +246,13 @@ export class SandyPluginInstance extends BasePluginInstance {
disconnect() { disconnect() {
this.assertNotDestroyed(); this.assertNotDestroyed();
if (this.connected.get()) { if (this.connected.get()) {
const {serverAddOn, name} = this.definition.details;
if (serverAddOn) {
this.serverAddOnControls.stop(name).catch((e) => {
console.warn('Failed to stop a server add on', name, e);
});
}
this.connected.set(false); this.connected.set(false);
this.events.emit('disconnect'); this.events.emit('disconnect');
} }

View File

@@ -14,6 +14,7 @@ import {
BundledPluginDetails, BundledPluginDetails,
fsConstants, fsConstants,
InstalledPluginDetails, InstalledPluginDetails,
ServerAddOnControls,
} from 'flipper-common'; } from 'flipper-common';
import { import {
@@ -116,6 +117,11 @@ interface BasePluginResult {
* Trigger menu entry by label * Trigger menu entry by label
*/ */
triggerMenuEntry(label: string): void; triggerMenuEntry(label: string): void;
// TODO: Refine server add-on test methods
/**
* Communication with a server add-on
*/
serverAddOnControls: ServerAddOnControls;
} }
interface StartPluginResult<Module extends FlipperPluginModule<any>> interface StartPluginResult<Module extends FlipperPluginModule<any>>
@@ -254,7 +260,10 @@ export function startPlugin<Module extends FlipperPluginModule<any>>(
}, },
}; };
const serverAddOnControls = createServerAddOnControlsMock();
const pluginInstance = new SandyPluginInstance( const pluginInstance = new SandyPluginInstance(
serverAddOnControls,
flipperUtils, flipperUtils,
definition, definition,
fakeFlipperClient, fakeFlipperClient,
@@ -263,7 +272,7 @@ export function startPlugin<Module extends FlipperPluginModule<any>>(
); );
const res: StartPluginResult<Module> = { const res: StartPluginResult<Module> = {
...createBasePluginResult(pluginInstance), ...createBasePluginResult(pluginInstance, serverAddOnControls),
instance: pluginInstance.instanceApi, instance: pluginInstance.instanceApi,
module, module,
connect: () => pluginInstance.connect(), connect: () => pluginInstance.connect(),
@@ -282,6 +291,7 @@ export function startPlugin<Module extends FlipperPluginModule<any>>(
pluginInstance.receiveMessages(messages as any); pluginInstance.receiveMessages(messages as any);
}); });
}, },
serverAddOnControls,
}; };
(res as any)._backingInstance = pluginInstance; (res as any)._backingInstance = pluginInstance;
// we start activated // we start activated
@@ -337,7 +347,9 @@ export function startDevicePlugin<Module extends FlipperDevicePluginModule>(
const flipperLib = createMockFlipperLib(options); const flipperLib = createMockFlipperLib(options);
const testDevice = createMockDevice(options); const testDevice = createMockDevice(options);
const serverAddOnControls = createServerAddOnControlsMock();
const pluginInstance = new SandyDevicePluginInstance( const pluginInstance = new SandyDevicePluginInstance(
serverAddOnControls,
flipperLib, flipperLib,
definition, definition,
testDevice, testDevice,
@@ -346,7 +358,7 @@ export function startDevicePlugin<Module extends FlipperDevicePluginModule>(
); );
const res: StartDevicePluginResult<Module> = { const res: StartDevicePluginResult<Module> = {
...createBasePluginResult(pluginInstance), ...createBasePluginResult(pluginInstance, serverAddOnControls),
module, module,
instance: pluginInstance.instanceApi, instance: pluginInstance.instanceApi,
sendLogEntry: (entry) => { sendLogEntry: (entry) => {
@@ -444,6 +456,7 @@ export function createMockFlipperLib(options?: StartPluginOptions): FlipperLib {
function createBasePluginResult( function createBasePluginResult(
pluginInstance: BasePluginInstance, pluginInstance: BasePluginInstance,
serverAddOnControls: ServerAddOnControls,
): BasePluginResult { ): BasePluginResult {
return { return {
flipperLib: pluginInstance.flipperLib, flipperLib: pluginInstance.flipperLib,
@@ -469,6 +482,7 @@ function createBasePluginResult(
} }
entry.handler(); entry.handler();
}, },
serverAddOnControls,
}; };
} }
@@ -628,3 +642,10 @@ export function createFlipperServerMock(
close: createStubFunction(), close: createStubFunction(),
}; };
} }
function createServerAddOnControlsMock(): ServerAddOnControls {
return {
start: createStubFunction(),
stop: createStubFunction(),
};
}

View File

@@ -12,7 +12,7 @@
import {PluginDefinition} from './plugin'; import {PluginDefinition} from './plugin';
import BaseDevice from './devices/BaseDevice'; import BaseDevice from './devices/BaseDevice';
import {Logger} from 'flipper-common'; import {Logger, FlipperServer, ServerAddOnControls} from 'flipper-common';
import {Store} from './reducers/index'; import {Store} from './reducers/index';
import { import {
reportPluginFailures, reportPluginFailures,
@@ -46,6 +46,7 @@ import {
registerFlipperDebugMessage, registerFlipperDebugMessage,
} from './chrome/FlipperMessages'; } from './chrome/FlipperMessages';
import {waitFor} from './utils/waitFor'; import {waitFor} from './utils/waitFor';
import {createServerAddOnControls} from './utils/createServerAddOnControls';
type Plugins = Set<string>; type Plugins = Set<string>;
type PluginsArr = Array<string>; type PluginsArr = Array<string>;
@@ -124,6 +125,8 @@ export default class Client extends EventEmitter {
} }
> = {}; > = {};
sandyPluginStates = new Map<string /*pluginID*/, _SandyPluginInstance>(); sandyPluginStates = new Map<string /*pluginID*/, _SandyPluginInstance>();
private readonly serverAddOnControls: ServerAddOnControls;
private readonly flipperServer: Pick<FlipperServer, 'exec'>;
constructor( constructor(
id: string, id: string,
@@ -133,6 +136,7 @@ export default class Client extends EventEmitter {
store: Store, store: Store,
plugins: Plugins | null | undefined, plugins: Plugins | null | undefined,
device: BaseDevice, device: BaseDevice,
flipperServer: Pick<FlipperServer, 'exec'>,
) { ) {
super(); super();
this.connected.set(!!conn); this.connected.set(!!conn);
@@ -148,6 +152,8 @@ export default class Client extends EventEmitter {
this.broadcastCallbacks = new Map(); this.broadcastCallbacks = new Map();
this.activePlugins = new Set(); this.activePlugins = new Set();
this.device = device; this.device = device;
this.flipperServer = flipperServer;
this.serverAddOnControls = createServerAddOnControls(this.flipperServer);
} }
supportsPlugin(pluginId: string): boolean { supportsPlugin(pluginId: string): boolean {
@@ -218,6 +224,7 @@ export default class Client extends EventEmitter {
this.sandyPluginStates.set( this.sandyPluginStates.set(
plugin.id, plugin.id,
new _SandyPluginInstance( new _SandyPluginInstance(
this.serverAddOnControls,
getFlipperLib(), getFlipperLib(),
plugin, plugin,
this, this,

View File

@@ -198,6 +198,7 @@ export default class MockFlipper {
this._store, this._store,
new Set(supportedPlugins), new Set(supportedPlugins),
device, device,
this.flipperServer,
); );
client.rawCall = async ( client.rawCall = async (
method: string, method: string,

View File

@@ -24,10 +24,12 @@ import {
DeviceDescription, DeviceDescription,
FlipperServer, FlipperServer,
CrashLog, CrashLog,
ServerAddOnControls,
} from 'flipper-common'; } from 'flipper-common';
import {DeviceSpec, PluginDetails} from 'flipper-common'; import {DeviceSpec, PluginDetails} from 'flipper-common';
import {getPluginKey} from '../utils/pluginKey'; import {getPluginKey} from '../utils/pluginKey';
import {Base64} from 'js-base64'; import {Base64} from 'js-base64';
import {createServerAddOnControls} from '../utils/createServerAddOnControls';
type PluginDefinition = _SandyPluginDefinition; type PluginDefinition = _SandyPluginDefinition;
type PluginMap = Map<string, PluginDefinition>; type PluginMap = Map<string, PluginDefinition>;
@@ -45,10 +47,12 @@ export default class BaseDevice implements Device {
flipperServer: FlipperServer; flipperServer: FlipperServer;
isArchived = false; isArchived = false;
hasDevicePlugins = false; // true if there are device plugins for this device (not necessarily enabled) hasDevicePlugins = false; // true if there are device plugins for this device (not necessarily enabled)
private readonly serverAddOnControls: ServerAddOnControls;
constructor(flipperServer: FlipperServer, description: DeviceDescription) { constructor(flipperServer: FlipperServer, description: DeviceDescription) {
this.flipperServer = flipperServer; this.flipperServer = flipperServer;
this.description = description; this.description = description;
this.serverAddOnControls = createServerAddOnControls(this.flipperServer);
} }
get isConnected(): boolean { get isConnected(): boolean {
@@ -349,6 +353,7 @@ export default class BaseDevice implements Device {
this.sandyPluginStates.set( this.sandyPluginStates.set(
plugin.id, plugin.id,
new _SandyDevicePluginInstance( new _SandyDevicePluginInstance(
this.serverAddOnControls,
getFlipperLib(), getFlipperLib(),
plugin, plugin,
this, this,

View File

@@ -301,6 +301,7 @@ export async function handleClientConnected(
store, store,
undefined, undefined,
device, device,
server,
); );
console.debug( console.debug(

View File

@@ -38,6 +38,7 @@ import {
import {selectPlugin, getAllClients} from '../../reducers/connections'; import {selectPlugin, getAllClients} from '../../reducers/connections';
import {TestIdler} from '../Idler'; import {TestIdler} from '../Idler';
import {TestDevice} from '../../devices/TestDevice'; import {TestDevice} from '../../devices/TestDevice';
import {FlipperServer} from 'flipper-common';
const testIdler = new TestIdler(); const testIdler = new TestIdler();
@@ -65,6 +66,14 @@ const logger = {
}; };
const mockStore = configureStore<State, {}>([])(); const mockStore = configureStore<State, {}>([])();
const flipperServer = {
connect: async () => {},
on: () => {},
off: () => {},
close: () => {},
exec: () => {},
} as FlipperServer;
function generateNotifications( function generateNotifications(
id: string, id: string,
title: string, title: string,
@@ -725,6 +734,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
mockStore, mockStore,
new Set(['TestPlugin', 'TestDevicePlugin']), new Set(['TestPlugin', 'TestDevicePlugin']),
device1, device1,
flipperServer,
); );
const client2 = new Client( const client2 = new Client(
generateClientIdentifier(device1, 'app2'), generateClientIdentifier(device1, 'app2'),
@@ -739,6 +749,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
mockStore, mockStore,
new Set(['TestDevicePlugin']), new Set(['TestDevicePlugin']),
device1, device1,
flipperServer,
); );
const client3 = new Client( const client3 = new Client(
generateClientIdentifier(device1, 'app3'), generateClientIdentifier(device1, 'app3'),
@@ -753,6 +764,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
mockStore, mockStore,
new Set(['TestPlugin', 'TestDevicePlugin']), new Set(['TestPlugin', 'TestDevicePlugin']),
device1, device1,
flipperServer,
); );
const plugins: PluginsState = { const plugins: PluginsState = {
clientPlugins: new Map([ clientPlugins: new Map([
@@ -812,6 +824,7 @@ test('test determinePluginsToProcess for no selected plugin present in any clien
mockStore, mockStore,
new Set(['TestPlugin', 'TestDevicePlugin']), new Set(['TestPlugin', 'TestDevicePlugin']),
device1, device1,
flipperServer,
); );
const client2 = new Client( const client2 = new Client(
generateClientIdentifier(device1, 'app2'), generateClientIdentifier(device1, 'app2'),
@@ -826,6 +839,7 @@ test('test determinePluginsToProcess for no selected plugin present in any clien
mockStore, mockStore,
new Set(['TestDevicePlugin']), new Set(['TestDevicePlugin']),
device1, device1,
flipperServer,
); );
const plugins: PluginsState = { const plugins: PluginsState = {
clientPlugins: new Map([ clientPlugins: new Map([
@@ -868,6 +882,7 @@ test('test determinePluginsToProcess for multiple clients on same device', async
mockStore, mockStore,
new Set(['TestPlugin', 'TestDevicePlugin']), new Set(['TestPlugin', 'TestDevicePlugin']),
device1, device1,
flipperServer,
); );
const client2 = new Client( const client2 = new Client(
generateClientIdentifier(device1, 'app2'), generateClientIdentifier(device1, 'app2'),
@@ -882,6 +897,7 @@ test('test determinePluginsToProcess for multiple clients on same device', async
mockStore, mockStore,
new Set(['TestDevicePlugin']), new Set(['TestDevicePlugin']),
device1, device1,
flipperServer,
); );
const plugins: PluginsState = { const plugins: PluginsState = {
clientPlugins: new Map([['TestPlugin', TestPlugin]]), clientPlugins: new Map([['TestPlugin', TestPlugin]]),
@@ -929,6 +945,7 @@ test('test determinePluginsToProcess for multiple clients on different device',
mockStore, mockStore,
new Set(['TestPlugin', 'TestDevicePlugin']), new Set(['TestPlugin', 'TestDevicePlugin']),
device1, device1,
flipperServer,
); );
const client2Device1 = new Client( const client2Device1 = new Client(
generateClientIdentifier(device1, 'app2'), generateClientIdentifier(device1, 'app2'),
@@ -943,6 +960,7 @@ test('test determinePluginsToProcess for multiple clients on different device',
mockStore, mockStore,
new Set(['TestDevicePlugin']), new Set(['TestDevicePlugin']),
device1, device1,
flipperServer,
); );
const client1Device2 = new Client( const client1Device2 = new Client(
generateClientIdentifier(device2, 'app'), generateClientIdentifier(device2, 'app'),
@@ -957,6 +975,7 @@ test('test determinePluginsToProcess for multiple clients on different device',
mockStore, mockStore,
new Set(['TestPlugin', 'TestDevicePlugin']), new Set(['TestPlugin', 'TestDevicePlugin']),
device1, device1,
flipperServer,
); );
const client2Device2 = new Client( const client2Device2 = new Client(
generateClientIdentifier(device2, 'app2'), generateClientIdentifier(device2, 'app2'),
@@ -971,6 +990,7 @@ test('test determinePluginsToProcess for multiple clients on different device',
mockStore, mockStore,
new Set(['TestDevicePlugin']), new Set(['TestDevicePlugin']),
device1, device1,
flipperServer,
); );
const plugins: PluginsState = { const plugins: PluginsState = {
clientPlugins: new Map([['TestPlugin', TestPlugin]]), clientPlugins: new Map([['TestPlugin', TestPlugin]]),
@@ -1042,6 +1062,7 @@ test('test determinePluginsToProcess to ignore archived clients', async () => {
mockStore, mockStore,
new Set(['TestPlugin', 'TestDevicePlugin']), new Set(['TestPlugin', 'TestDevicePlugin']),
archivedDevice, archivedDevice,
flipperServer,
); );
const archivedClient = new Client( const archivedClient = new Client(
generateClientIdentifier(archivedDevice, 'app'), generateClientIdentifier(archivedDevice, 'app'),
@@ -1056,6 +1077,7 @@ test('test determinePluginsToProcess to ignore archived clients', async () => {
mockStore, mockStore,
new Set(['TestPlugin', 'TestDevicePlugin']), new Set(['TestPlugin', 'TestDevicePlugin']),
archivedDevice, archivedDevice,
flipperServer,
); );
const plugins: PluginsState = { const plugins: PluginsState = {
clientPlugins: new Map([['TestPlugin', TestPlugin]]), clientPlugins: new Map([['TestPlugin', TestPlugin]]),

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import {FlipperServer, ServerAddOnControls} from 'flipper-common';
export const createServerAddOnControls = (
flipperServer: Pick<FlipperServer, 'exec'>,
): ServerAddOnControls => ({
start: (pluginName) =>
flipperServer.exec('plugins-server-add-on-start', pluginName),
stop: (pluginName) =>
flipperServer.exec('plugins-server-add-on-stop', pluginName),
});

View File

@@ -575,6 +575,7 @@ export function importDataToStore(source: string, data: string, store: Store) {
store, store,
clientPlugins, clientPlugins,
archivedDevice, archivedDevice,
getRenderHostInstance().flipperServer,
).initFromImport(sandyPluginStates), ).initFromImport(sandyPluginStates),
}); });
}); });