Persist device ID

Summary: Linked to https://github.com/facebook/flipper/issues/3319

Reviewed By: passy

Differential Revision: D36786952

fbshipit-source-id: f3214f35039845a8e35fa14e63f509a6fbdddb1f
This commit is contained in:
Andrey Goncharov
2022-06-01 02:49:25 -07:00
committed by Facebook GitHub Bot
parent ee64216725
commit 055b14c6dd
5 changed files with 40 additions and 6 deletions

View File

@@ -24,8 +24,8 @@ if the corresponding desktop plugin is selected in the Flipper Desktop. The full
plugin API is documented plugin API is documented
[here](https://fbflipper.com/docs/extending/create-plugin). [here](https://fbflipper.com/docs/extending/create-plugin).
- `start` method. It starts the client. It has two arguments: - `start` method. It starts the client. It has two arguments:
- `appName` - (required) the name dsplayed in Flipper - `appName` - (required) the name displayed in Flipper
- `options` which conforms to the infterface - `options` which conforms to the interface
```ts ```ts
interface FlipperClientOptions { interface FlipperClientOptions {
// Make the client connect to a different URL // Make the client connect to a different URL
@@ -36,6 +36,8 @@ plugin API is documented
onError?: (e: unknown) => void; onError?: (e: unknown) => void;
// Timeout after which client tries to reconnect to Flipper // Timeout after which client tries to reconnect to Flipper
reconnectTimeout?: number; reconnectTimeout?: number;
// Set device ID. Default: random ID persisted to local storage.
getDeviceId?: () => Promise<string> | string
} }
``` ```
@@ -54,7 +56,7 @@ The sources of the corresponding Desktop plugin can be found
## Node.js ## Node.js
Node.js does not have a built-in WebSocket implementation. You need to install Node.js does not have a built-in WebSocket implementation. You need to install
any implmentation of WebSockets for Node.js that is compatible with the any implementation of WebSockets for Node.js that is compatible with the
interface of the interface of the
[web version](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket). [web version](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket).

View File

@@ -11,7 +11,12 @@ import {FlipperConnection} from './connection';
import {FlipperRequest, FlipperResponse} from './message'; import {FlipperRequest, FlipperResponse} from './message';
import {FlipperPlugin} from './plugin'; import {FlipperPlugin} from './plugin';
import {FlipperResponder} from './responder'; import {FlipperResponder} from './responder';
import {assert, detectDevice, detectOS} from './util'; import {
assert,
detectDevice,
detectOS,
getDeviceId as getDeviceIdDefault,
} from './util';
import {RECONNECT_TIMEOUT} from './consts'; import {RECONNECT_TIMEOUT} from './consts';
// TODO: Share with flipper-server-core // TODO: Share with flipper-server-core
@@ -133,13 +138,15 @@ interface FlipperClientOptions {
onError?: (e: unknown) => void; onError?: (e: unknown) => void;
// Timeout after which client tries to reconnect to Flipper // Timeout after which client tries to reconnect to Flipper
reconnectTimeout?: number; reconnectTimeout?: number;
// Set device ID. Default: random ID persisted to local storage.
getDeviceId?: () => Promise<string> | string;
} }
export class FlipperClient { export class FlipperClient {
private readonly plugins: Map<string, FlipperPlugin> = new Map(); private readonly plugins: Map<string, FlipperPlugin> = new Map();
private readonly connections: Map<string, FlipperConnection> = new Map(); private readonly connections: Map<string, FlipperConnection> = new Map();
private ws?: FlipperWebSocket; private ws?: FlipperWebSocket;
private readonly devicePseudoId = `${Date.now()}.${Math.random()}`; private deviceId!: string;
private readonly os = detectOS(); private readonly os = detectOS();
private readonly device = detectDevice(); private readonly device = detectDevice();
private reconnectionTimer?: NodeJS.Timeout; private reconnectionTimer?: NodeJS.Timeout;
@@ -171,12 +178,14 @@ export class FlipperClient {
websocketFactory = (url) => new WebSocket(url) as FlipperWebSocket, websocketFactory = (url) => new WebSocket(url) as FlipperWebSocket,
onError = (e) => console.error('WebSocket error', e), onError = (e) => console.error('WebSocket error', e),
reconnectTimeout = RECONNECT_TIMEOUT, reconnectTimeout = RECONNECT_TIMEOUT,
getDeviceId = getDeviceIdDefault,
}: FlipperClientOptions = {}, }: FlipperClientOptions = {},
): Promise<void> { ): Promise<void> {
if (this.ws) { if (this.ws) {
return; return;
} }
this.deviceId = await getDeviceId();
this.appName = appName; this.appName = appName;
this.onError = onError; this.onError = onError;
this.urlBase = urlBase; this.urlBase = urlBase;
@@ -218,7 +227,7 @@ export class FlipperClient {
} }
private connectToFlipper() { private connectToFlipper() {
const url = `ws://${this.urlBase}?device_id=${this.device}${this.devicePseudoId}&device=${this.device}&app=${this.appName}&os=${this.os}`; const url = `ws://${this.urlBase}?device_id=${this.device}${this.deviceId}&device=${this.device}&app=${this.appName}&os=${this.os}`;
const encodedUrl = encodeURI(url); const encodedUrl = encodeURI(url);
this.ws = this.websocketFactory(encodedUrl); this.ws = this.websocketFactory(encodedUrl);

View File

@@ -8,3 +8,4 @@
*/ */
export const RECONNECT_TIMEOUT = 1000; export const RECONNECT_TIMEOUT = 1000;
export const DEVICE_ID_STORAGE_KEY = 'js-flipper-device-id';

View File

@@ -9,6 +9,7 @@
import {FlipperClient} from './client'; import {FlipperClient} from './client';
export * from './consts';
export * from './client'; export * from './client';
export * from './plugin'; export * from './plugin';

View File

@@ -7,6 +7,8 @@
* @format * @format
*/ */
import {DEVICE_ID_STORAGE_KEY} from './consts';
// https://github.com/microsoft/TypeScript/issues/36931#issuecomment-846131999 // https://github.com/microsoft/TypeScript/issues/36931#issuecomment-846131999
type Assert = (condition: unknown) => asserts condition; type Assert = (condition: unknown) => asserts condition;
export const assert: Assert = (condition) => { export const assert: Assert = (condition) => {
@@ -66,3 +68,22 @@ export const detectDevice = (): string => {
} }
return require('os').release(); return require('os').release();
}; };
export const getDeviceId = () => {
// localStorage is not defined in Node.js env
const persistedId =
typeof localStorage === 'object'
? localStorage?.getItem(DEVICE_ID_STORAGE_KEY)
: undefined;
if (persistedId) {
return persistedId;
}
const newId = `${Date.now()}.${Math.random()}`;
if (typeof localStorage === 'object') {
localStorage?.setItem(DEVICE_ID_STORAGE_KEY, newId);
}
return newId;
};