Add support for async / custom plugin export

Summary:
Sandy plugins can now set up an `onExport` handler to enable customizing the export format of a plugin: `client.onExport(callback: (idler, onStatusMessage) => Promise<state>)`

Import will be done in next diff

Reviewed By: nikoant

Differential Revision: D26124440

fbshipit-source-id: c787c79d929aa8fb484f15a9340d7c87545793cb
This commit is contained in:
Michel Weststrate
2021-02-01 11:40:20 -08:00
committed by Facebook GitHub Bot
parent 32bde8cace
commit 34c915a739
21 changed files with 264 additions and 77 deletions

View File

@@ -14,6 +14,12 @@ import {MenuEntry, NormalizedMenuEntry, normalizeMenuEntry} from './MenuEntry';
import {FlipperLib} from './FlipperLib';
import {Device, RealFlipperDevice} from './DevicePlugin';
import {batched} from '../state/batch';
import {Idler} from '../utils/Idler';
type StateExportHandler = (
idler: Idler,
onStatusMessage: (msg: string) => void,
) => Promise<Record<string, any>>;
export interface BasePluginClient {
readonly device: Device;
@@ -38,6 +44,12 @@ export interface BasePluginClient {
*/
onDeepLink(cb: (deepLink: unknown) => void): void;
/**
* Triggered when the current plugin is being exported and should create a snapshot of the state exported.
* Overrides the default export behavior and ignores any 'persist' flags of state.
*/
onExport(exporter: StateExportHandler): void;
/**
* Register menu entries in the Flipper toolbar
*/
@@ -88,6 +100,8 @@ export abstract class BasePluginInstance {
rootStates: Record<string, Atom<any>> = {};
// last seen deeplink
lastDeeplink?: any;
// export handler
exportHandler?: StateExportHandler;
menuEntries: NormalizedMenuEntry[] = [];
@@ -145,6 +159,12 @@ export abstract class BasePluginInstance {
onDestroy: (cb) => {
this.events.on('destroy', batched(cb));
},
onExport: (cb) => {
if (this.exportHandler) {
throw new Error('onExport handler already set');
}
this.exportHandler = cb;
},
addMenuEntry: (...entries) => {
for (const entry of entries) {
const normalized = normalizeMenuEntry(entry);
@@ -204,14 +224,30 @@ export abstract class BasePluginInstance {
}
}
exportState() {
exportStateSync() {
// This method is mainly intended for unit testing
if (this.exportHandler) {
throw new Error(
'Cannot export sync a plugin that does have an export handler',
);
}
return Object.fromEntries(
Object.entries(this.rootStates).map(([key, atom]) => [key, atom.get()]),
);
}
async exportState(
idler: Idler,
onStatusMessage: (msg: string) => void,
): Promise<Record<string, any>> {
if (this.exportHandler) {
return await this.exportHandler(idler, onStatusMessage);
}
return this.exportStateSync();
}
isPersistable(): boolean {
return Object.keys(this.rootStates).length > 0;
return !!this.exportHandler || Object.keys(this.rootStates).length > 0;
}
protected assertNotDestroyed() {