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:
committed by
Facebook GitHub Bot
parent
32bde8cace
commit
34c915a739
@@ -59,6 +59,7 @@ test('Correct top level API exposed', () => {
|
||||
"DeviceType",
|
||||
"Draft",
|
||||
"FlipperLib",
|
||||
"Idler",
|
||||
"LogLevel",
|
||||
"LogTypes",
|
||||
"Logger",
|
||||
|
||||
@@ -69,6 +69,7 @@ export {
|
||||
useLogger,
|
||||
_LoggerContext,
|
||||
} from './utils/Logger';
|
||||
export {Idler} from './utils/Idler';
|
||||
|
||||
// It's not ideal that this exists in flipper-plugin sources directly,
|
||||
// but is the least pain for plugin authors.
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
import {BasePluginInstance} from '../plugin/PluginBase';
|
||||
import {FlipperLib} from '../plugin/FlipperLib';
|
||||
import {stubLogger} from '../utils/Logger';
|
||||
import {Idler} from '../utils/Idler';
|
||||
|
||||
type Renderer = RenderResult<typeof queries>;
|
||||
|
||||
@@ -95,9 +96,14 @@ interface BasePluginResult {
|
||||
triggerDeepLink(deeplink: unknown): void;
|
||||
|
||||
/**
|
||||
* Grab all the persistable state
|
||||
* Grab all the persistable state, but will ignore any onExport handler
|
||||
*/
|
||||
exportState(): any;
|
||||
exportState(): Record<string, any>;
|
||||
|
||||
/**
|
||||
* Grab all the persistable state, respecting onExport handlers
|
||||
*/
|
||||
exportStateAsync(): Promise<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Trigger menu entry by label
|
||||
@@ -367,7 +373,9 @@ function createBasePluginResult(
|
||||
flipperLib: pluginInstance.flipperLib,
|
||||
activate: () => pluginInstance.activate(),
|
||||
deactivate: () => pluginInstance.deactivate(),
|
||||
exportState: () => pluginInstance.exportState(),
|
||||
exportStateAsync: () =>
|
||||
pluginInstance.exportState(createStubIdler(), () => {}),
|
||||
exportState: () => pluginInstance.exportStateSync(),
|
||||
triggerDeepLink: (deepLink: unknown) => {
|
||||
pluginInstance.triggerDeepLink(deepLink);
|
||||
},
|
||||
@@ -422,3 +430,18 @@ function createMockDevice(options?: StartPluginOptions): RealFlipperDevice {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createStubIdler(): Idler {
|
||||
return {
|
||||
shouldIdle() {
|
||||
return false;
|
||||
},
|
||||
idle() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
cancel() {},
|
||||
isCancelled() {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
15
desktop/flipper-plugin/src/utils/Idler.tsx
Normal file
15
desktop/flipper-plugin/src/utils/Idler.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
export interface Idler {
|
||||
shouldIdle(): boolean;
|
||||
idle(): Promise<void>;
|
||||
cancel(): void;
|
||||
isCancelled(): boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user