Decouple JS device from Store

Summary: Made a start with decoupling JS device. Incomplete as there are still Electron deps.

Reviewed By: timur-valiev

Differential Revision: D30309257

fbshipit-source-id: b8002170cbbe8d68e1795ce7c12ffce4c8eac853
This commit is contained in:
Michel Weststrate
2021-08-17 07:50:43 -07:00
committed by Facebook GitHub Bot
parent a9c6351cf0
commit 3736cbc480
4 changed files with 63 additions and 89 deletions

View File

@@ -10,14 +10,14 @@
import React from 'react';
import {Store} from '../reducers/index';
import {Logger} from '../fb-interfaces/Logger';
import {startFlipperServer} from '../server/FlipperServer';
import {FlipperServer} from '../server/FlipperServer';
import {selectClient, selectDevice} from '../reducers/connections';
import Client from '../Client';
import {notification} from 'antd';
export default async (store: Store, logger: Logger) => {
const {enableAndroid, androidHome} = store.getState().settingsState;
const server = startFlipperServer(
const server = new FlipperServer(
{
enableAndroid,
androidHome,
@@ -101,7 +101,7 @@ export default async (store: Store, logger: Logger) => {
}
server
.waitForServerStarted()
.start()
.then(() => {
console.log(
'Flipper server started and accepting device / client connections',

View File

@@ -48,18 +48,18 @@ export interface FlipperServerConfig {
serverPorts: ServerPorts;
}
export function startFlipperServer(
config: FlipperServerConfig,
store: Store,
logger: Logger,
): FlipperServer {
const server = new FlipperServer(config, store, logger);
server.start();
return server;
}
type ServerState = 'pending' | 'starting' | 'started' | 'error' | 'closed';
// defaultConfig should be used for testing only, and disables by default all features
const defaultConfig: FlipperServerConfig = {
androidHome: '',
enableAndroid: false,
serverPorts: {
insecure: -1,
secure: -1,
},
};
/**
* FlipperServer takes care of all incoming device & client connections.
* It will set up managers per device type, and create the incoming
@@ -69,7 +69,10 @@ type ServerState = 'pending' | 'starting' | 'started' | 'error' | 'closed';
* using '.on'. All events are strongly typed.
*/
export class FlipperServer {
public config: FlipperServerConfig;
private readonly events = new EventEmitter();
// server handles the incoming RSocket / WebSocket connections from Flipper clients
readonly server: ServerController;
readonly disposers: ((() => void) | void)[] = [];
private readonly devices = new Map<string, BaseDevice>();
@@ -78,60 +81,14 @@ export class FlipperServer {
// TODO: remove store argument
constructor(
public config: FlipperServerConfig,
config: Partial<FlipperServerConfig>,
/** @deprecated remove! */
public store: Store,
public logger: Logger,
) {
this.server = new ServerController(logger, store);
this.config = {...defaultConfig, ...config};
const server = (this.server = new ServerController(this));
this.android = new AndroidDeviceManager(this);
}
setServerState(state: ServerState, error?: Error) {
this.state = state;
this.emit('server-state', {state, error});
}
async waitForServerStarted() {
return new Promise<void>((resolve, reject) => {
switch (this.state) {
case 'closed':
return reject(new Error('Server was closed already'));
case 'error':
return reject(new Error('Server has errored already'));
case 'started':
return resolve();
default: {
const listener = ({
state,
error,
}: {
state: ServerState;
error: Error;
}) => {
switch (state) {
case 'error':
return reject(error);
case 'started':
return resolve();
case 'closed':
return reject(new Error('Server closed'));
}
this.events.off('server-state', listener);
};
this.events.on('server-state', listener);
}
}
});
}
/** @private */
async start() {
if (this.state !== 'pending') {
throw new Error('Server already started');
}
this.setServerState('starting');
const server = this.server;
server.addListener('new-client', (client: Client) => {
this.emit('client-connected', client);
@@ -214,9 +171,24 @@ export class FlipperServer {
);
},
);
}
setServerState(state: ServerState, error?: Error) {
this.state = state;
this.emit('server-state', {state, error});
}
/**
* Starts listening to parts and watching for devices
*/
async start() {
if (this.state !== 'pending') {
throw new Error('Server already started');
}
this.setServerState('starting');
try {
await server.init();
await this.server.init();
await this.startDeviceListeners();
this.setServerState('started');
} catch (e) {

View File

@@ -34,6 +34,7 @@ import ServerAdapter, {
ServerEventsListener,
} from './ServerAdapter';
import {createBrowserServer, createServer} from './ServerFactory';
import {FlipperServer} from '../FlipperServer';
type ClientInfo = {
connection: ClientConnection | null | undefined;
@@ -70,29 +71,38 @@ class ServerController extends EventEmitter implements ServerEventsListener {
certificateProvider: CertificateProvider;
connectionTracker: ConnectionTracker;
logger: Logger;
store: Store;
flipperServer: FlipperServer;
timeHandler: NodeJS.Timeout | undefined;
constructor(logger: Logger, store: Store) {
constructor(flipperServer: FlipperServer) {
super();
this.logger = logger;
this.flipperServer = flipperServer;
this.connections = new Map();
this.certificateProvider = new CertificateProvider(
this,
logger,
store.getState().settingsState,
this.logger,
this.store.getState().settingsState,
);
this.connectionTracker = new ConnectionTracker(logger);
this.connectionTracker = new ConnectionTracker(this.logger);
this.secureServer = null;
this.insecureServer = null;
this.browserServer = null;
this.initialized = null;
this.store = store;
this.timeHandler = undefined;
}
get logger(): Logger {
return this.flipperServer.logger;
}
/**
* @deprecated
*/
get store(): Store {
return this.flipperServer.store;
}
/**
* Loads the secure server configuration and starts any necessary servers.
* Initialisation is complete once the initialized promise is fullfilled at
@@ -117,7 +127,7 @@ class ServerController extends EventEmitter implements ServerEventsListener {
reportPlatformFailures(this.initialized, 'initializeServer');
if (GK.get('flipper_js_client_emulator')) {
initJsEmulatorIPC(this.store, this.logger, this, this.connections);
initJsEmulatorIPC(this.flipperServer, this.connections);
}
return this.initialized;

View File

@@ -16,11 +16,8 @@ import {
} from '../../comms/ClientConnection';
import {ipcRenderer, remote, IpcRendererEvent} from 'electron';
import JSDevice from './JSDevice';
import {Store} from '../../../reducers';
import {Logger} from '../../../fb-interfaces/Logger';
import ServerController from '../../comms/ServerController';
import {buildClientId} from '../../../utils/clientUtils';
import {destroyDevice} from '../../../reducers/connections';
import {FlipperServer} from '../../FlipperServer';
const connections: Map<number, JSClientFlipperConnection> = new Map();
@@ -31,9 +28,7 @@ function jsDeviceId(windowId: number): string {
}
export function initJsEmulatorIPC(
store: Store,
logger: Logger,
flipperServer: ServerController,
flipperServer: FlipperServer,
flipperConnections: Map<
string,
{
@@ -48,10 +43,7 @@ export function initJsEmulatorIPC(
const {windowId} = message;
const {plugins, appName} = message.payload;
const device = new JSDevice(jsDeviceId(windowId), 'jsEmulator', windowId);
store.dispatch({
type: 'REGISTER_DEVICE',
payload: device,
});
flipperServer.registerDevice(device);
const connection = new JSClientFlipperConnection(windowId);
connections.set(windowId, connection);
@@ -70,8 +62,8 @@ export function initJsEmulatorIPC(
clientId,
query,
connection,
logger,
store,
flipperServer.logger,
flipperServer.store,
plugins,
device,
);
@@ -87,8 +79,8 @@ export function initJsEmulatorIPC(
status == ConnectionStatus.CLOSED
) {
console.debug(`Device disconnected ${client.id}`, 'server');
flipperServer.removeConnection(client.id);
destroyDevice(store, logger, jsDeviceId(windowId));
flipperServer.server.removeConnection(client.id);
flipperServer.unregisterDevice(jsDeviceId(windowId));
connections.delete(windowId);
availablePlugins.delete(windowId);
}
@@ -98,8 +90,8 @@ export function initJsEmulatorIPC(
.init()
.then(() => {
console.log(client);
flipperServer.emit('new-client', client);
flipperServer.emit('clients-change');
flipperServer.server.emit('new-client', client);
flipperServer.server.emit('clients-change');
client.emit('plugins-change');
ipcRenderer.on(