Add exec Node API to FlipperLib
Summary: Changelog: Allow flipper plugins to run "exec" Node API on Flipper server. Reviewed By: mweststrate Differential Revision: D32881149 fbshipit-source-id: 46486a47ee9824ca68897c19fd86b4afc7f8bf1d
This commit is contained in:
committed by
Facebook GitHub Bot
parent
8ca2c59499
commit
e458ae76f9
@@ -132,6 +132,13 @@ export type IOSDeviceParams = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type FlipperServerCommands = {
|
export type FlipperServerCommands = {
|
||||||
|
/**
|
||||||
|
* @throws ExecError
|
||||||
|
*/
|
||||||
|
'node-api-exec': (
|
||||||
|
command: string,
|
||||||
|
options?: ExecOptions & {encoding?: BufferEncoding},
|
||||||
|
) => Promise<ExecOut<string>>;
|
||||||
'get-config': () => Promise<FlipperServerConfig>;
|
'get-config': () => Promise<FlipperServerConfig>;
|
||||||
'get-changelog': () => Promise<string>;
|
'get-changelog': () => Promise<string>;
|
||||||
'device-list': () => Promise<DeviceDescription[]>;
|
'device-list': () => Promise<DeviceDescription[]>;
|
||||||
@@ -270,6 +277,38 @@ type ENVIRONMENT_PATHS =
|
|||||||
| 'tempPath'
|
| 'tempPath'
|
||||||
| 'desktopPath';
|
| 'desktopPath';
|
||||||
|
|
||||||
|
export interface ExecOptions {
|
||||||
|
maxBuffer?: number;
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExecError {
|
||||||
|
message: string;
|
||||||
|
stdout: string;
|
||||||
|
stderr: string;
|
||||||
|
stack?: string;
|
||||||
|
cmd?: string;
|
||||||
|
killed?: boolean;
|
||||||
|
code?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExecOut<StdOutErrType> {
|
||||||
|
stdout: StdOutErrType;
|
||||||
|
stderr: StdOutErrType;
|
||||||
|
}
|
||||||
|
export type BufferEncoding =
|
||||||
|
| 'ascii'
|
||||||
|
| 'utf8'
|
||||||
|
| 'utf-8'
|
||||||
|
| 'utf16le'
|
||||||
|
| 'ucs2'
|
||||||
|
| 'ucs-2'
|
||||||
|
| 'base64'
|
||||||
|
| 'base64url'
|
||||||
|
| 'latin1'
|
||||||
|
| 'binary'
|
||||||
|
| 'hex';
|
||||||
|
|
||||||
export type FlipperServerConfig = {
|
export type FlipperServerConfig = {
|
||||||
gatekeepers: Record<string, boolean>;
|
gatekeepers: Record<string, boolean>;
|
||||||
env: Partial<Record<ENVIRONMENT_VARIABLES, string>>;
|
env: Partial<Record<ENVIRONMENT_VARIABLES, string>>;
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ test('Correct top level API exposed', () => {
|
|||||||
"NormalizedMenuEntry",
|
"NormalizedMenuEntry",
|
||||||
"Notification",
|
"Notification",
|
||||||
"PluginClient",
|
"PluginClient",
|
||||||
|
"RemoteNodeAPI",
|
||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export {
|
|||||||
setFlipperLibImplementation as _setFlipperLibImplementation,
|
setFlipperLibImplementation as _setFlipperLibImplementation,
|
||||||
FileDescriptor,
|
FileDescriptor,
|
||||||
FileEncoding,
|
FileEncoding,
|
||||||
|
RemoteNodeAPI,
|
||||||
} from './plugin/FlipperLib';
|
} from './plugin/FlipperLib';
|
||||||
export {
|
export {
|
||||||
MenuEntry,
|
MenuEntry,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {NormalizedMenuEntry} from './MenuEntry';
|
|||||||
import {RealFlipperClient} from './Plugin';
|
import {RealFlipperClient} from './Plugin';
|
||||||
import {Notification} from './Notification';
|
import {Notification} from './Notification';
|
||||||
import {DetailSidebarProps} from '../ui/DetailSidebar';
|
import {DetailSidebarProps} from '../ui/DetailSidebar';
|
||||||
|
import {ExecOptions, ExecOut, BufferEncoding} from 'flipper-common';
|
||||||
|
|
||||||
export type FileEncoding = 'utf-8' | 'base64';
|
export type FileEncoding = 'utf-8' | 'base64';
|
||||||
|
|
||||||
@@ -22,6 +23,18 @@ export interface FileDescriptor {
|
|||||||
path?: string;
|
path?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RemoteNodeAPI = {
|
||||||
|
childProcess: {
|
||||||
|
exec(
|
||||||
|
command: string,
|
||||||
|
options?: {encoding?: BufferEncoding} & ExecOptions,
|
||||||
|
): Promise<ExecOut<string>>;
|
||||||
|
};
|
||||||
|
fs: {
|
||||||
|
// TODO: Fill me
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface exposes all global methods for which an implementation will be provided by Flipper itself
|
* This interface exposes all global methods for which an implementation will be provided by Flipper itself
|
||||||
*/
|
*/
|
||||||
@@ -105,6 +118,7 @@ export interface FlipperLib {
|
|||||||
homePath: string;
|
homePath: string;
|
||||||
appPath: string;
|
appPath: string;
|
||||||
};
|
};
|
||||||
|
removeNodeAPI: RemoteNodeAPI;
|
||||||
}
|
}
|
||||||
|
|
||||||
export let flipperLibInstance: FlipperLib | undefined;
|
export let flipperLibInstance: FlipperLib | undefined;
|
||||||
|
|||||||
@@ -389,6 +389,12 @@ export function createMockFlipperLib(options?: StartPluginOptions): FlipperLib {
|
|||||||
appPath: process.cwd(),
|
appPath: process.cwd(),
|
||||||
homePath: `/dev/null`,
|
homePath: `/dev/null`,
|
||||||
},
|
},
|
||||||
|
removeNodeAPI: {
|
||||||
|
childProcess: {
|
||||||
|
exec: jest.fn(),
|
||||||
|
},
|
||||||
|
fs: {},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import {Logger} from 'flipper-common';
|
|
||||||
import ServerController from './comms/ServerController';
|
import ServerController from './comms/ServerController';
|
||||||
import {CertificateExchangeMedium} from './utils/CertificateProvider';
|
import {CertificateExchangeMedium} from './utils/CertificateProvider';
|
||||||
import {AndroidDeviceManager} from './devices/android/androidDeviceManager';
|
import {AndroidDeviceManager} from './devices/android/androidDeviceManager';
|
||||||
@@ -25,6 +24,7 @@ import {
|
|||||||
FlipperServer,
|
FlipperServer,
|
||||||
UninitializedClient,
|
UninitializedClient,
|
||||||
FlipperServerConfig,
|
FlipperServerConfig,
|
||||||
|
Logger,
|
||||||
} from 'flipper-common';
|
} from 'flipper-common';
|
||||||
import {ServerDevice} from './devices/ServerDevice';
|
import {ServerDevice} from './devices/ServerDevice';
|
||||||
import {Base64} from 'js-base64';
|
import {Base64} from 'js-base64';
|
||||||
@@ -43,6 +43,7 @@ import {
|
|||||||
internGraphGETAPIRequest,
|
internGraphGETAPIRequest,
|
||||||
internGraphPOSTAPIRequest,
|
internGraphPOSTAPIRequest,
|
||||||
} from './fb-stubs/internRequests';
|
} from './fb-stubs/internRequests';
|
||||||
|
import {commandNodeApiExec} from './commands/NodeApiExec';
|
||||||
|
|
||||||
export const SERVICE_FLIPPER = 'flipper.oAuthToken';
|
export const SERVICE_FLIPPER = 'flipper.oAuthToken';
|
||||||
|
|
||||||
@@ -213,6 +214,7 @@ export class FlipperServerImpl implements FlipperServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private commandHandler: FlipperServerCommands = {
|
private commandHandler: FlipperServerCommands = {
|
||||||
|
'node-api-exec': commandNodeApiExec,
|
||||||
'get-config': async () => this.config,
|
'get-config': async () => this.config,
|
||||||
'get-changelog': getChangelog,
|
'get-changelog': getChangelog,
|
||||||
'device-list': async () => {
|
'device-list': async () => {
|
||||||
|
|||||||
39
desktop/flipper-server-core/src/commands/NodeApiExec.tsx
Normal file
39
desktop/flipper-server-core/src/commands/NodeApiExec.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {ExecError, FlipperServerCommands} from 'flipper-common';
|
||||||
|
import {exec} from 'child_process';
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
export const commandNodeApiExec: FlipperServerCommands['node-api-exec'] =
|
||||||
|
async (command, options) =>
|
||||||
|
new Promise((resolve, reject) =>
|
||||||
|
exec(command, options, (error, stdout, stderr) => {
|
||||||
|
assert(typeof stdout === 'string');
|
||||||
|
assert(typeof stderr === 'string');
|
||||||
|
if (error) {
|
||||||
|
const wrappedError: ExecError = {
|
||||||
|
message: error.message,
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
cmd: error.cmd,
|
||||||
|
killed: error.killed,
|
||||||
|
code: error.code,
|
||||||
|
stack: error.stack,
|
||||||
|
};
|
||||||
|
reject(wrappedError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
@@ -36,6 +36,7 @@ export function startSocketServer(
|
|||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
|
// TODO: Serialize error
|
||||||
client.emit('exec-response-error', id, error.toString());
|
client.emit('exec-response-error', id, error.toString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export function createFlipperServer(): Promise<FlipperServer> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('exec-response-error', (id: number, error: any) => {
|
socket.on('exec-response-error', (id: number, error: any) => {
|
||||||
|
// TODO: Deserialize error
|
||||||
console.debug('exec <<< [SERVER ERROR]', id, error);
|
console.debug('exec <<< [SERVER ERROR]', id, error);
|
||||||
const entry = pendingRequests.get(id);
|
const entry = pendingRequests.get(id);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {_setFlipperLibImplementation} from 'flipper-plugin';
|
import {_setFlipperLibImplementation, RemoteNodeAPI} from 'flipper-plugin';
|
||||||
import type {Logger} from 'flipper-common';
|
import type {BufferEncoding, ExecOptions, Logger} from 'flipper-common';
|
||||||
import type {Store} from '../reducers';
|
import type {Store} from '../reducers';
|
||||||
import createPaste from '../fb-stubs/createPaste';
|
import createPaste from '../fb-stubs/createPaste';
|
||||||
import type BaseDevice from '../devices/BaseDevice';
|
import type BaseDevice from '../devices/BaseDevice';
|
||||||
@@ -63,5 +63,19 @@ export function initializeFlipperLibImplementation(
|
|||||||
appPath: renderHost.serverConfig.paths.appPath,
|
appPath: renderHost.serverConfig.paths.appPath,
|
||||||
homePath: renderHost.serverConfig.paths.homePath,
|
homePath: renderHost.serverConfig.paths.homePath,
|
||||||
},
|
},
|
||||||
|
removeNodeAPI: {
|
||||||
|
childProcess: {
|
||||||
|
exec: (async (
|
||||||
|
command: string,
|
||||||
|
options?: ExecOptions & {encoding?: BufferEncoding},
|
||||||
|
) =>
|
||||||
|
renderHost.flipperServer.exec(
|
||||||
|
'node-api-exec',
|
||||||
|
command,
|
||||||
|
options,
|
||||||
|
)) as RemoteNodeAPI['childProcess']['exec'],
|
||||||
|
},
|
||||||
|
fs: {},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user