Implement sending messages from a server add-on

Reviewed By: mweststrate

Differential Revision: D34074383

fbshipit-source-id: de85e7a22dc9bb780163fc5b522708e8bc976df3
This commit is contained in:
Andrey Goncharov
2022-02-28 03:50:34 -08:00
committed by Facebook GitHub Bot
parent 842b2c810a
commit db976d5113
6 changed files with 82 additions and 19 deletions

View File

@@ -125,9 +125,7 @@ export type FlipperServerEvents = {
id: string;
message: string;
};
'plugins-server-add-on-message': {
payload: ExecuteMessage;
};
'plugins-server-add-on-message': ExecuteMessage;
'download-file-update': DownloadFileUpdate;
};

View File

@@ -88,7 +88,7 @@ export class FlipperServerImpl implements FlipperServer {
// given flipper-dump, it might make more sense to have the plugin command
// handling (like download, install, etc) moved to flipper-server & app,
// but let's keep things simple for now
this.pluginManager = new PluginManager();
this.pluginManager = new PluginManager(this);
server.addListener('error', (err) => {
this.emit('server-error', err);

View File

@@ -32,6 +32,7 @@ import {
installPluginFromNpm,
} from 'flipper-plugin-lib';
import {ServerAddOn} from './ServerAddOn';
import {FlipperServerForServerAddOn} from './ServerAddOnDesktopToModuleConnection';
const maxInstalledPluginVersionsToKeep = 2;
@@ -50,6 +51,8 @@ const isExecuteMessage = (message: object): message is ExecuteMessage =>
export class PluginManager {
private readonly serverAddOns = new Map<string, ServerAddOn>();
constructor(private readonly flipperServer: FlipperServerForServerAddOn) {}
async start() {
// This needn't happen immediately and is (light) I/O work.
setTimeout(() => {
@@ -183,8 +186,11 @@ export class PluginManager {
return;
}
const newServerAddOn = await ServerAddOn.start(pluginName, owner, () =>
this.serverAddOns.delete(pluginName),
const newServerAddOn = await ServerAddOn.start(
pluginName,
owner,
() => this.serverAddOns.delete(pluginName),
this.flipperServer,
);
this.serverAddOns.set(pluginName, newServerAddOn);
}

View File

@@ -9,13 +9,17 @@
import assert from 'assert';
import {assertNotNull} from '../comms/Utilities';
import {ServerAddOnDesktopToModuleConnection} from './ServerAddOnDesktopToModuleConnection';
import {
ServerAddOnDesktopToModuleConnection,
FlipperServerForServerAddOn,
} from './ServerAddOnDesktopToModuleConnection';
import {ServerAddOnModuleToDesktopConnection} from './ServerAddOnModuleToDesktopConnection';
type ServerAddOnCleanup = () => Promise<void>;
interface ServerAddOnModule {
serverAddOn?: (
connection: ServerAddOnModuleToDesktopConnection,
{flipperServer}: {flipperServer: FlipperServerForServerAddOn},
) => Promise<ServerAddOnCleanup>;
}
@@ -34,13 +38,14 @@ export class ServerAddOn {
public readonly connection: ServerAddOnDesktopToModuleConnection,
initialOwner: string,
) {
this.owners = new Set(initialOwner);
this.owners = new Set([initialOwner]);
}
static async start(
pluginName: string,
initialOwner: string,
onStop: () => void,
flipperServer: FlipperServerForServerAddOn,
): Promise<ServerAddOn> {
console.info('ServerAddOn.start', pluginName);
@@ -54,7 +59,9 @@ export class ServerAddOn {
const serverAddOnModuleToDesktopConnection =
new ServerAddOnModuleToDesktopConnection();
const cleanup = await serverAddOn(serverAddOnModuleToDesktopConnection);
const cleanup = await serverAddOn(serverAddOnModuleToDesktopConnection, {
flipperServer,
});
assert(
typeof cleanup === 'function',
`ServerAddOn ${pluginName} must return a clean up function, instead it returned ${typeof cleanup}.`,
@@ -65,12 +72,15 @@ export class ServerAddOn {
await cleanup();
};
const desktopToModuleConnection = new ServerAddOnDesktopToModuleConnection(
serverAddOnModuleToDesktopConnection,
flipperServer,
);
return new ServerAddOn(
pluginName,
onStopCombined,
new ServerAddOnDesktopToModuleConnection(
serverAddOnModuleToDesktopConnection,
),
desktopToModuleConnection,
initialOwner,
);
}

View File

@@ -8,13 +8,37 @@
*/
import assert from 'assert';
import {ClientResponseType, ExecuteMessage} from 'flipper-common';
import {ServerAddOnModuleToDesktopConnection} from './ServerAddOnModuleToDesktopConnection';
import {
ClientResponseType,
ExecuteMessage,
FlipperServer,
FlipperServerEvents,
} from 'flipper-common';
import {ServerDevice} from '../devices/ServerDevice';
import {
ServerAddOnModuleToDesktopConnection,
ServerAddOnModuleToDesktopConnectionEvents,
} from './ServerAddOnModuleToDesktopConnection';
export interface FlipperServerForServerAddOn extends FlipperServer {
emit(
event: 'plugins-server-add-on-message',
payload: FlipperServerEvents['plugins-server-add-on-message'],
): void;
registerDevice(device: ServerDevice): void;
unregisterDevice(serial: string): void;
getDevice(serial: string): ServerDevice;
getDeviceSerials(): string[];
getDevices(): ServerDevice[];
}
export class ServerAddOnDesktopToModuleConnection {
constructor(
private readonly moduleToDesktopConnection: ServerAddOnModuleToDesktopConnection,
) {}
private readonly flipperServer: FlipperServerForServerAddOn,
) {
this.subscribeToMessagesFromServerAddOn();
}
async sendExpectResponse({
method,
@@ -34,4 +58,15 @@ export class ServerAddOnDesktopToModuleConnection {
length,
};
}
private subscribeToMessagesFromServerAddOn() {
const event = 'message';
const onMessage = (
message: ServerAddOnModuleToDesktopConnectionEvents[typeof event],
) => {
this.flipperServer.emit('plugins-server-add-on-message', message);
};
this.moduleToDesktopConnection.on(event, onMessage);
}
}

View File

@@ -7,7 +7,8 @@
* @format
*/
import {ResponseMessage, ClientErrorType} from 'flipper-common';
import EventEmitter from 'events';
import {ResponseMessage, ClientErrorType, ExecuteMessage} from 'flipper-common';
import {safeJSONStringify} from '../utils/safeJSONStringify';
// TODO: Share with js-flipper? Is it worth it?
@@ -24,11 +25,24 @@ type FlipperPluginReceiver = (
data: any,
) => FlipperPluginReceiverRes | Promise<FlipperPluginReceiverRes>;
export class ServerAddOnModuleToDesktopConnection {
export type ServerAddOnModuleToDesktopConnectionEvents = {
message: ExecuteMessage;
};
export class ServerAddOnModuleToDesktopConnection extends EventEmitter {
private subscriptions: Map<string, FlipperPluginReceiver> = new Map();
send() {
// TODO: Implement me
send(method: string, params: unknown) {
const event = 'message';
const message: ServerAddOnModuleToDesktopConnectionEvents[typeof event] = {
method: 'execute',
params: {
method,
params,
api: '', // TODO: Consider using here pluginId and validate it
},
};
this.emit('message', message);
}
receive(method: string, receiver: FlipperPluginReceiver) {