Add close button to the export share sheet
Summary: Adds close button to the dialog box which appears while exporting a flipper trace. To implement this I had to first see what is the bottle neck of the export process. The major bottle neck turned out to be the serialization step. So to make the export process interruptible, I have put in a call `await idler.idle()` which resolves when the main thread is idle. I have also added the tests for the idler. Reviewed By: passy Differential Revision: D16183582 fbshipit-source-id: 4ec0c985e216fd9d41e91cdcd8b4cca66d2cb04d
This commit is contained in:
committed by
Facebook Github Bot
parent
c63e6cffeb
commit
7f2709e1a5
@@ -272,7 +272,7 @@ async function startFlipper(userArguments: UserArguments) {
|
|||||||
> = [
|
> = [
|
||||||
(userArguments: UserArguments) => {
|
(userArguments: UserArguments) => {
|
||||||
if (userArguments.listDevices) {
|
if (userArguments.listDevices) {
|
||||||
return listDevices().then((devices: Array<BaseDevice>) => {
|
return listDevices().then(async (devices: Array<BaseDevice>) => {
|
||||||
const mapped = devices.map(device => {
|
const mapped = devices.map(device => {
|
||||||
return {
|
return {
|
||||||
os: device.os,
|
os: device.os,
|
||||||
@@ -281,7 +281,7 @@ async function startFlipper(userArguments: UserArguments) {
|
|||||||
serial: device.serial,
|
serial: device.serial,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return {exit: true, result: serialize(mapped)};
|
return {exit: true, result: await serialize(mapped)};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Promise.resolve({exit: false});
|
return Promise.resolve({exit: false});
|
||||||
@@ -332,12 +332,14 @@ async function startFlipper(userArguments: UserArguments) {
|
|||||||
const exitActionClosures: Array<
|
const exitActionClosures: Array<
|
||||||
(userArguments: UserArguments, store: Store) => Promise<Action>,
|
(userArguments: UserArguments, store: Store) => Promise<Action>,
|
||||||
> = [
|
> = [
|
||||||
(userArguments: UserArguments, store: Store) => {
|
async (userArguments: UserArguments, store: Store) => {
|
||||||
const {listPlugins} = userArguments;
|
const {listPlugins} = userArguments;
|
||||||
if (listPlugins) {
|
if (listPlugins) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
exit: true,
|
exit: true,
|
||||||
result: serialize(getActivePluginNames(store.getState().plugins)),
|
result: await serialize(
|
||||||
|
getActivePluginNames(store.getState().plugins),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
} from 'flipper';
|
} from 'flipper';
|
||||||
import type {Logger} from '../fb-interfaces/Logger.js';
|
import type {Logger} from '../fb-interfaces/Logger.js';
|
||||||
|
import {Idler} from '../utils/Idler';
|
||||||
import {shareFlipperData} from '../fb-stubs/user';
|
import {shareFlipperData} from '../fb-stubs/user';
|
||||||
import {exportStore, EXPORT_FLIPPER_TRACE_EVENT} from '../utils/exportData.js';
|
import {exportStore, EXPORT_FLIPPER_TRACE_EVENT} from '../utils/exportData.js';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
@@ -91,12 +92,14 @@ export default class ShareSheet extends Component<Props, State> {
|
|||||||
result: null,
|
result: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
idler = new Idler();
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const mark = 'shareSheetExportUrl';
|
const mark = 'shareSheetExportUrl';
|
||||||
performance.mark(mark);
|
performance.mark(mark);
|
||||||
try {
|
try {
|
||||||
const {serializedString, errorArray} = await reportPlatformFailures(
|
const {serializedString, errorArray} = await reportPlatformFailures(
|
||||||
exportStore(this.context.store),
|
exportStore(this.context.store, this.idler),
|
||||||
`${EXPORT_FLIPPER_TRACE_EVENT}:UI_LINK`,
|
`${EXPORT_FLIPPER_TRACE_EVENT}:UI_LINK`,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -115,10 +118,11 @@ export default class ShareSheet extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
this.props.logger.trackTimeSince(mark, 'export:url-success');
|
this.props.logger.trackTimeSince(mark, 'export:url-success');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
const str = e instanceof Error ? e.toString() : e;
|
||||||
this.setState({
|
this.setState({
|
||||||
result: {
|
result: {
|
||||||
error_class: 'EXPORT_ERROR',
|
error_class: 'EXPORT_ERROR',
|
||||||
error: e,
|
error: str,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.props.logger.trackTimeSince(mark, 'export:url-error');
|
this.props.logger.trackTimeSince(mark, 'export:url-error');
|
||||||
@@ -126,6 +130,10 @@ export default class ShareSheet extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const onHide = () => {
|
||||||
|
this.props.onHide();
|
||||||
|
this.idler.cancel();
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
{this.state.result ? (
|
{this.state.result ? (
|
||||||
@@ -160,18 +168,26 @@ export default class ShareSheet extends Component<Props, State> {
|
|||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
<FlexRow>
|
<FlexRow>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<Button compact padded onClick={this.props.onHide}>
|
<Button compact padded onClick={onHide}>
|
||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
<FlexColumn>
|
||||||
<Center>
|
<Center>
|
||||||
<LoadingIndicator size={30} />
|
<LoadingIndicator size={30} />
|
||||||
<Uploading bold color={colors.macOSTitleBarIcon}>
|
<Uploading bold color={colors.macOSTitleBarIcon}>
|
||||||
Uploading Flipper trace...
|
Uploading Flipper trace...
|
||||||
</Uploading>
|
</Uploading>
|
||||||
</Center>
|
</Center>
|
||||||
|
<FlexRow>
|
||||||
|
<Spacer />
|
||||||
|
<Button compact padded onClick={onHide}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</FlexRow>
|
||||||
|
</FlexColumn>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {reportPlatformFailures} from '../utils/metrics';
|
|||||||
// $FlowFixMe: Missing type defs for node built-in.
|
// $FlowFixMe: Missing type defs for node built-in.
|
||||||
import {performance} from 'perf_hooks';
|
import {performance} from 'perf_hooks';
|
||||||
import type {Logger} from '../fb-interfaces/Logger.js';
|
import type {Logger} from '../fb-interfaces/Logger.js';
|
||||||
|
import {Idler} from '../utils/Idler';
|
||||||
import {
|
import {
|
||||||
exportStoreToFile,
|
exportStoreToFile,
|
||||||
EXPORT_FLIPPER_TRACE_EVENT,
|
EXPORT_FLIPPER_TRACE_EVENT,
|
||||||
@@ -82,6 +83,8 @@ export default class ShareSheetExportFile extends Component<Props, State> {
|
|||||||
result: null,
|
result: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
idler = new Idler();
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const mark = 'shareSheetExportFile';
|
const mark = 'shareSheetExportFile';
|
||||||
performance.mark(mark);
|
performance.mark(mark);
|
||||||
@@ -92,7 +95,7 @@ export default class ShareSheetExportFile extends Component<Props, State> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {errorArray} = await reportPlatformFailures(
|
const {errorArray} = await reportPlatformFailures(
|
||||||
exportStoreToFile(this.props.file, this.context.store),
|
exportStoreToFile(this.props.file, this.context.store, this.idler),
|
||||||
`${EXPORT_FLIPPER_TRACE_EVENT}:UI_FILE`,
|
`${EXPORT_FLIPPER_TRACE_EVENT}:UI_FILE`,
|
||||||
);
|
);
|
||||||
this.setState({errorArray, result: {success: true, error: null}});
|
this.setState({errorArray, result: {success: true, error: null}});
|
||||||
@@ -104,10 +107,14 @@ export default class ShareSheetExportFile extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.file) {
|
const onHide = () => {
|
||||||
return this.renderNoFileError();
|
this.props.onHide();
|
||||||
}
|
this.idler.cancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!this.props.file) {
|
||||||
|
return this.renderNoFileError(onHide);
|
||||||
|
}
|
||||||
const {result} = this.state;
|
const {result} = this.state;
|
||||||
if (result) {
|
if (result) {
|
||||||
const {success, error} = result;
|
const {success, error} = result;
|
||||||
@@ -125,7 +132,7 @@ export default class ShareSheetExportFile extends Component<Props, State> {
|
|||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
<FlexRow>
|
<FlexRow>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<Button compact padded onClick={this.props.onHide}>
|
<Button compact padded onClick={onHide}>
|
||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
@@ -141,7 +148,7 @@ export default class ShareSheetExportFile extends Component<Props, State> {
|
|||||||
</ErrorMessage>
|
</ErrorMessage>
|
||||||
<FlexRow>
|
<FlexRow>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<Button compact padded onClick={this.props.onHide}>
|
<Button compact padded onClick={onHide}>
|
||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
@@ -158,12 +165,18 @@ export default class ShareSheetExportFile extends Component<Props, State> {
|
|||||||
Exporting Flipper trace...
|
Exporting Flipper trace...
|
||||||
</Uploading>
|
</Uploading>
|
||||||
</Center>
|
</Center>
|
||||||
|
<FlexRow>
|
||||||
|
<Spacer />
|
||||||
|
<Button compact padded onClick={onHide}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</FlexRow>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNoFileError() {
|
renderNoFileError(onHide: () => void) {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Center>
|
<Center>
|
||||||
@@ -171,7 +184,7 @@ export default class ShareSheetExportFile extends Component<Props, State> {
|
|||||||
</Center>
|
</Center>
|
||||||
<FlexRow>
|
<FlexRow>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<Button compact padded onClick={this.props.onHide}>
|
<Button compact padded onClick={onHide}>
|
||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
|
|||||||
33
src/utils/Idler.js
Normal file
33
src/utils/Idler.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
export class Idler {
|
||||||
|
lastIdle: number;
|
||||||
|
interval: number;
|
||||||
|
kill: boolean;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.lastIdle = 0;
|
||||||
|
this.interval = 3;
|
||||||
|
this.kill = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
idle(): Promise<void> {
|
||||||
|
if (this.kill) {
|
||||||
|
throw new Error('Idler got killed');
|
||||||
|
}
|
||||||
|
const now = performance.now();
|
||||||
|
if (now - this.lastIdle > this.interval) {
|
||||||
|
this.lastIdle = now;
|
||||||
|
return new Promise(resolve => setTimeout(resolve, 0));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this.kill = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/utils/__tests__/Idler.node.js
Normal file
24
src/utils/__tests__/Idler.node.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Idler} from '../Idler';
|
||||||
|
|
||||||
|
test('Idler should interrupt', async () => {
|
||||||
|
const idler = new Idler();
|
||||||
|
let i = 0;
|
||||||
|
try {
|
||||||
|
for (; i < 500; i++) {
|
||||||
|
if (i == 100) {
|
||||||
|
idler.cancel();
|
||||||
|
}
|
||||||
|
await idler.idle();
|
||||||
|
}
|
||||||
|
fail('Idler should have thrown an error');
|
||||||
|
} catch (e) {
|
||||||
|
expect(i).toEqual(100);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -18,21 +18,21 @@ class TestObject extends Object {
|
|||||||
map: ?Map<any, any>;
|
map: ?Map<any, any>;
|
||||||
set: ?Set<any>;
|
set: ?Set<any>;
|
||||||
}
|
}
|
||||||
test('test makeObjectSerializable function for unnested object with no Set and Map', () => {
|
test('test makeObjectSerializable function for unnested object with no Set and Map', async () => {
|
||||||
const obj = {key1: 'value1', key2: 'value2'};
|
const obj = {key1: 'value1', key2: 'value2'};
|
||||||
const output = makeObjectSerializable(obj);
|
const output = await makeObjectSerializable(obj);
|
||||||
expect(output).toEqual(obj);
|
expect(output).toEqual(obj);
|
||||||
|
|
||||||
// Testing numbers
|
// Testing numbers
|
||||||
const obj2 = {key1: 1, key2: 2};
|
const obj2 = {key1: 1, key2: 2};
|
||||||
const output2 = makeObjectSerializable(obj2);
|
const output2 = await makeObjectSerializable(obj2);
|
||||||
expect(output2).toEqual(obj2);
|
expect(output2).toEqual(obj2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('makeObjectSerializable function for unnested object with values which returns false when put in an if condition', () => {
|
test('makeObjectSerializable function for unnested object with values which returns false when put in an if condition', async () => {
|
||||||
const obj2 = {key1: 0, key2: ''};
|
const obj2 = {key1: 0, key2: ''};
|
||||||
const output2 = makeObjectSerializable(obj2);
|
const output2 = await makeObjectSerializable(obj2);
|
||||||
expect(output2).toEqual(obj2);
|
return expect(output2).toEqual(obj2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test deserializeObject function for unnested object with no Set and Map', () => {
|
test('test deserializeObject function for unnested object with no Set and Map', () => {
|
||||||
@@ -46,25 +46,25 @@ test('test deserializeObject function for unnested object with no Set and Map',
|
|||||||
expect(output2).toEqual(obj2);
|
expect(output2).toEqual(obj2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test makeObjectSerializable and deserializeObject function for nested object with no Set and Map', () => {
|
test('test makeObjectSerializable and deserializeObject function for nested object with no Set and Map', async () => {
|
||||||
const subObj = {key1: 'value1', key2: 'value2'};
|
const subObj = {key1: 'value1', key2: 'value2'};
|
||||||
const subObj2 = {key21: 'value21', key22: 'value22'};
|
const subObj2 = {key21: 'value21', key22: 'value22'};
|
||||||
const obj = {key1: subObj, key2: subObj2};
|
const obj = {key1: subObj, key2: subObj2};
|
||||||
const output = makeObjectSerializable(obj);
|
const output = await makeObjectSerializable(obj);
|
||||||
expect(output).toEqual(obj);
|
expect(output).toEqual(obj);
|
||||||
expect(deserializeObject(output)).toEqual(obj);
|
expect(deserializeObject(output)).toEqual(obj);
|
||||||
|
|
||||||
const subObjNum = {key1: 1, key2: 2};
|
const subObjNum = {key1: 1, key2: 2};
|
||||||
const subObjNum2 = {key21: 21, key22: 22};
|
const subObjNum2 = {key21: 21, key22: 22};
|
||||||
const obj2 = {key1: subObjNum, key2: subObjNum2};
|
const obj2 = {key1: subObjNum, key2: subObjNum2};
|
||||||
const output2 = makeObjectSerializable(obj2);
|
const output2 = await makeObjectSerializable(obj2);
|
||||||
expect(output2).toEqual(obj2);
|
expect(output2).toEqual(obj2);
|
||||||
expect(deserializeObject(output2)).toEqual(obj2);
|
expect(deserializeObject(output2)).toEqual(obj2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test makeObjectSerializable and deserializeObject function for Map and Set with no nesting', () => {
|
test('test makeObjectSerializable and deserializeObject function for Map and Set with no nesting', async () => {
|
||||||
const map = new Map([['k1', 'v1'], ['k2', 'v2']]);
|
const map = new Map([['k1', 'v1'], ['k2', 'v2']]);
|
||||||
const output = makeObjectSerializable(map);
|
const output = await makeObjectSerializable(map);
|
||||||
const expected = {
|
const expected = {
|
||||||
__flipper_object_type__: 'Map',
|
__flipper_object_type__: 'Map',
|
||||||
data: [['k1', 'v1'], ['k2', 'v2']],
|
data: [['k1', 'v1'], ['k2', 'v2']],
|
||||||
@@ -73,7 +73,7 @@ test('test makeObjectSerializable and deserializeObject function for Map and Set
|
|||||||
expect(deserializeObject(output)).toEqual(map);
|
expect(deserializeObject(output)).toEqual(map);
|
||||||
|
|
||||||
const set = new Set([1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]);
|
const set = new Set([1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]);
|
||||||
const outputSet = makeObjectSerializable(set);
|
const outputSet = await makeObjectSerializable(set);
|
||||||
const expectedSet = {
|
const expectedSet = {
|
||||||
__flipper_object_type__: 'Set',
|
__flipper_object_type__: 'Set',
|
||||||
data: [1, 2, 3, 4, 5, 6],
|
data: [1, 2, 3, 4, 5, 6],
|
||||||
@@ -82,12 +82,12 @@ test('test makeObjectSerializable and deserializeObject function for Map and Set
|
|||||||
expect(deserializeObject(outputSet)).toEqual(set);
|
expect(deserializeObject(outputSet)).toEqual(set);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test makeObjectSerializable and deserializeObject function for Map and Set with nesting', () => {
|
test('test makeObjectSerializable and deserializeObject function for Map and Set with nesting', async () => {
|
||||||
const map = new Map([
|
const map = new Map([
|
||||||
[{title: 'k1'}, {title: 'v1'}],
|
[{title: 'k1'}, {title: 'v1'}],
|
||||||
[{title: 'k2'}, {title: 'v2'}],
|
[{title: 'k2'}, {title: 'v2'}],
|
||||||
]);
|
]);
|
||||||
const output = makeObjectSerializable(map);
|
const output = await makeObjectSerializable(map);
|
||||||
const expected = {
|
const expected = {
|
||||||
__flipper_object_type__: 'Map',
|
__flipper_object_type__: 'Map',
|
||||||
data: [[{title: 'k1'}, {title: 'v1'}], [{title: 'k2'}, {title: 'v2'}]],
|
data: [[{title: 'k1'}, {title: 'v1'}], [{title: 'k2'}, {title: 'v2'}]],
|
||||||
@@ -103,7 +103,7 @@ test('test makeObjectSerializable and deserializeObject function for Map and Set
|
|||||||
{title: '5'},
|
{title: '5'},
|
||||||
{title: '6'},
|
{title: '6'},
|
||||||
]);
|
]);
|
||||||
const outputSet = makeObjectSerializable(set);
|
const outputSet = await makeObjectSerializable(set);
|
||||||
const expectedSet = {
|
const expectedSet = {
|
||||||
__flipper_object_type__: 'Set',
|
__flipper_object_type__: 'Set',
|
||||||
data: [
|
data: [
|
||||||
@@ -119,14 +119,14 @@ test('test makeObjectSerializable and deserializeObject function for Map and Set
|
|||||||
expect(deserializeObject(outputSet)).toEqual(set);
|
expect(deserializeObject(outputSet)).toEqual(set);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test makeObjectSerializable and deserializeObject function for custom Object', () => {
|
test('test makeObjectSerializable and deserializeObject function for custom Object', async () => {
|
||||||
const obj = new TestObject('title');
|
const obj = new TestObject('title');
|
||||||
const output = makeObjectSerializable(obj);
|
const output = await makeObjectSerializable(obj);
|
||||||
expect(output).toEqual(obj);
|
expect(output).toEqual(obj);
|
||||||
expect(deserializeObject(output)).toEqual(obj);
|
expect(deserializeObject(output)).toEqual(obj);
|
||||||
|
|
||||||
const nestedObj = new TestObject({title: 'nestedTitle'});
|
const nestedObj = new TestObject({title: 'nestedTitle'});
|
||||||
const nestedoutput = makeObjectSerializable(nestedObj);
|
const nestedoutput = await makeObjectSerializable(nestedObj);
|
||||||
expect(nestedoutput).toEqual(nestedObj);
|
expect(nestedoutput).toEqual(nestedObj);
|
||||||
expect(deserializeObject(nestedoutput)).toEqual(nestedObj);
|
expect(deserializeObject(nestedoutput)).toEqual(nestedObj);
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ test('test makeObjectSerializable and deserializeObject function for custom Obje
|
|||||||
{title: 'nestedTitle'},
|
{title: 'nestedTitle'},
|
||||||
new Map([['k1', 'v1'], ['k2', 'v2']]),
|
new Map([['k1', 'v1'], ['k2', 'v2']]),
|
||||||
);
|
);
|
||||||
const nestedObjWithMapOutput = makeObjectSerializable(nestedObjWithMap);
|
const nestedObjWithMapOutput = await makeObjectSerializable(nestedObjWithMap);
|
||||||
const expectedNestedObjWithMapOutput = {
|
const expectedNestedObjWithMapOutput = {
|
||||||
title: {title: 'nestedTitle'},
|
title: {title: 'nestedTitle'},
|
||||||
map: {
|
map: {
|
||||||
@@ -158,7 +158,9 @@ test('test makeObjectSerializable and deserializeObject function for custom Obje
|
|||||||
{title: '6'},
|
{title: '6'},
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
const nestedObjWithMapSetOutput = makeObjectSerializable(nestedObjWithMapSet);
|
const nestedObjWithMapSetOutput = await makeObjectSerializable(
|
||||||
|
nestedObjWithMapSet,
|
||||||
|
);
|
||||||
const expectedNestedObjWithMapSetOutput = {
|
const expectedNestedObjWithMapSetOutput = {
|
||||||
title: {title: 'nestedTitle'},
|
title: {title: 'nestedTitle'},
|
||||||
map: {
|
map: {
|
||||||
@@ -183,9 +185,9 @@ test('test makeObjectSerializable and deserializeObject function for custom Obje
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test makeObjectSerializable and deserializeObject function for Array as input', () => {
|
test('test makeObjectSerializable and deserializeObject function for Array as input', async () => {
|
||||||
const arr = [1, 2, 4, 5];
|
const arr = [1, 2, 4, 5];
|
||||||
const output = makeObjectSerializable(arr);
|
const output = await makeObjectSerializable(arr);
|
||||||
expect(output).toEqual(arr);
|
expect(output).toEqual(arr);
|
||||||
expect(deserializeObject(output)).toEqual(arr);
|
expect(deserializeObject(output)).toEqual(arr);
|
||||||
|
|
||||||
@@ -214,36 +216,36 @@ test('test makeObjectSerializable and deserializeObject function for Array as in
|
|||||||
data: [['d1', 'v1'], ['d2', 'v2']],
|
data: [['d1', 'v1'], ['d2', 'v2']],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const outputMap = makeObjectSerializable(arrMap);
|
const outputMap = await makeObjectSerializable(arrMap);
|
||||||
expect(outputMap).toEqual(expectedArr);
|
expect(outputMap).toEqual(expectedArr);
|
||||||
expect(deserializeObject(outputMap)).toEqual(arrMap);
|
expect(deserializeObject(outputMap)).toEqual(arrMap);
|
||||||
|
|
||||||
const arrStr = ['first', 'second', 'third', 'fourth'];
|
const arrStr = ['first', 'second', 'third', 'fourth'];
|
||||||
const outputStr = makeObjectSerializable(arrStr);
|
const outputStr = await makeObjectSerializable(arrStr);
|
||||||
expect(outputStr).toEqual(arrStr);
|
expect(outputStr).toEqual(arrStr);
|
||||||
expect(deserializeObject(outputStr)).toEqual(arrStr);
|
expect(deserializeObject(outputStr)).toEqual(arrStr);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test serialize and deserializeObject function for non Object input', () => {
|
test('test serialize and deserializeObject function for non Object input', async () => {
|
||||||
expect(makeObjectSerializable('octopus')).toEqual('octopus');
|
expect(await makeObjectSerializable('octopus')).toEqual('octopus');
|
||||||
expect(deserializeObject(makeObjectSerializable('octopus'))).toEqual(
|
expect(deserializeObject(await makeObjectSerializable('octopus'))).toEqual(
|
||||||
'octopus',
|
'octopus',
|
||||||
);
|
);
|
||||||
expect(makeObjectSerializable(24567)).toEqual(24567);
|
expect(await makeObjectSerializable(24567)).toEqual(24567);
|
||||||
expect(deserializeObject(makeObjectSerializable(24567))).toEqual(24567);
|
expect(deserializeObject(await makeObjectSerializable(24567))).toEqual(24567);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test makeObjectSerializable and deserializeObject function for Date input', () => {
|
test('test makeObjectSerializable and deserializeObject function for Date input', async () => {
|
||||||
const date = new Date('2019-02-15');
|
const date = new Date('2019-02-15');
|
||||||
const expectedDate = {
|
const expectedDate = {
|
||||||
__flipper_object_type__: 'Date',
|
__flipper_object_type__: 'Date',
|
||||||
data: date.toString(),
|
data: date.toString(),
|
||||||
};
|
};
|
||||||
expect(makeObjectSerializable(date)).toEqual(expectedDate);
|
expect(await makeObjectSerializable(date)).toEqual(expectedDate);
|
||||||
expect(deserializeObject(makeObjectSerializable(date))).toEqual(date);
|
expect(deserializeObject(await makeObjectSerializable(date))).toEqual(date);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test makeObjectSerializable and deserializeObject function for Map of Sets', () => {
|
test('test makeObjectSerializable and deserializeObject function for Map of Sets', async () => {
|
||||||
const map = new Map([
|
const map = new Map([
|
||||||
['k1', new Set([1, 2, 3, 4, 5, 6])],
|
['k1', new Set([1, 2, 3, 4, 5, 6])],
|
||||||
[new Set([1, 2]), new Map([['k3', 'v3']])],
|
[new Set([1, 2]), new Map([['k3', 'v3']])],
|
||||||
@@ -258,11 +260,11 @@ test('test makeObjectSerializable and deserializeObject function for Map of Sets
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
expect(makeObjectSerializable(map)).toEqual(expectedOutput);
|
expect(await makeObjectSerializable(map)).toEqual(expectedOutput);
|
||||||
expect(deserializeObject(makeObjectSerializable(map))).toEqual(map);
|
expect(deserializeObject(await makeObjectSerializable(map))).toEqual(map);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test makeObjectSerializable and deserializeObject function for Map, Dates and Set with complex nesting', () => {
|
test('test makeObjectSerializable and deserializeObject function for Map, Dates and Set with complex nesting', async () => {
|
||||||
const date1 = new Date('2019-02-15');
|
const date1 = new Date('2019-02-15');
|
||||||
const date2 = new Date('2019-02-16');
|
const date2 = new Date('2019-02-16');
|
||||||
const map = new Map([['k1', date1], ['k2', new Set([date2])]]);
|
const map = new Map([['k1', date1], ['k2', new Set([date2])]]);
|
||||||
@@ -279,6 +281,6 @@ test('test makeObjectSerializable and deserializeObject function for Map, Dates
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
expect(makeObjectSerializable(map)).toEqual(expectedOutput);
|
expect(await makeObjectSerializable(map)).toEqual(expectedOutput);
|
||||||
expect(deserializeObject(makeObjectSerializable(map))).toEqual(map);
|
expect(deserializeObject(await makeObjectSerializable(map))).toEqual(map);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {readCurrentRevision} from './packageMetadata.js';
|
|||||||
import {tryCatchReportPlatformFailures} from './metrics';
|
import {tryCatchReportPlatformFailures} from './metrics';
|
||||||
import {promisify} from 'util';
|
import {promisify} from 'util';
|
||||||
import promiseTimeout from './promiseTimeout';
|
import promiseTimeout from './promiseTimeout';
|
||||||
|
import {Idler} from './Idler';
|
||||||
export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace';
|
export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace';
|
||||||
export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace';
|
export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace';
|
||||||
|
|
||||||
@@ -266,14 +266,12 @@ export async function getStoreExport(
|
|||||||
plugins.devicePlugins.forEach((val, key) => {
|
plugins.devicePlugins.forEach((val, key) => {
|
||||||
pluginsMap.set(key, val);
|
pluginsMap.set(key, val);
|
||||||
});
|
});
|
||||||
|
|
||||||
const metadata = await fetchMetadata(pluginStates, pluginsMap, store);
|
const metadata = await fetchMetadata(pluginStates, pluginsMap, store);
|
||||||
const {errorArray} = metadata;
|
const {errorArray} = metadata;
|
||||||
const newPluginState = metadata.pluginStates;
|
const newPluginState = metadata.pluginStates;
|
||||||
|
|
||||||
const {activeNotifications} = store.getState().notifications;
|
const {activeNotifications} = store.getState().notifications;
|
||||||
const {devicePlugins} = store.getState().plugins;
|
const {devicePlugins} = store.getState().plugins;
|
||||||
|
|
||||||
const exportData = await processStore(
|
const exportData = await processStore(
|
||||||
activeNotifications,
|
activeNotifications,
|
||||||
selectedDevice,
|
selectedDevice,
|
||||||
@@ -287,6 +285,7 @@ export async function getStoreExport(
|
|||||||
|
|
||||||
export function exportStore(
|
export function exportStore(
|
||||||
store: MiddlewareAPI,
|
store: MiddlewareAPI,
|
||||||
|
idler?: Idler,
|
||||||
): Promise<{serializedString: string, errorArray: Array<Error>}> {
|
): Promise<{serializedString: string, errorArray: Array<Error>}> {
|
||||||
getLogger().track('usage', EXPORT_FLIPPER_TRACE_EVENT);
|
getLogger().track('usage', EXPORT_FLIPPER_TRACE_EVENT);
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
@@ -296,7 +295,8 @@ export function exportStore(
|
|||||||
console.error('Make sure a device is connected');
|
console.error('Make sure a device is connected');
|
||||||
reject(new Error('No device is selected'));
|
reject(new Error('No device is selected'));
|
||||||
}
|
}
|
||||||
const serializedString = serialize(exportData);
|
try {
|
||||||
|
const serializedString = await serialize(exportData, idler);
|
||||||
if (serializedString.length <= 0) {
|
if (serializedString.length <= 0) {
|
||||||
reject(new Error('Serialize function returned empty string'));
|
reject(new Error('Serialize function returned empty string'));
|
||||||
}
|
}
|
||||||
@@ -304,14 +304,18 @@ export function exportStore(
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const exportStoreToFile = (
|
export const exportStoreToFile = (
|
||||||
exportFilePath: string,
|
exportFilePath: string,
|
||||||
store: Store,
|
store: Store,
|
||||||
|
idler?: Idler,
|
||||||
): Promise<{errorArray: Array<Error>}> => {
|
): Promise<{errorArray: Array<Error>}> => {
|
||||||
return exportStore(store).then(({serializedString, errorArray}) => {
|
return exportStore(store, idler).then(({serializedString, errorArray}) => {
|
||||||
return promisify(fs.writeFile)(exportFilePath, serializedString).then(
|
return promisify(fs.writeFile)(exportFilePath, serializedString).then(
|
||||||
() => {
|
() => {
|
||||||
return {errorArray};
|
return {errorArray};
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ async function exportMetrics(
|
|||||||
metrics[clientID] = mergedMetrics;
|
metrics[clientID] = mergedMetrics;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.resolve(serialize(metrics));
|
return Promise.resolve(await serialize(metrics));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function exportMetricsWithoutTrace(
|
export async function exportMetricsWithoutTrace(
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function serialize(obj: Object): string {
|
import {Idler} from './Idler';
|
||||||
return JSON.stringify(makeObjectSerializable(obj));
|
export async function serialize(obj: Object, idler?: Idler): Promise<string> {
|
||||||
|
return makeObjectSerializable(obj, idler).then(obj => JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deserialize(str: string): Object {
|
export function deserialize(str: string): Object {
|
||||||
@@ -111,14 +112,16 @@ export function processObjectToBeSerialized(
|
|||||||
}
|
}
|
||||||
return {childNeedsIteration, outputObject: obj};
|
return {childNeedsIteration, outputObject: obj};
|
||||||
}
|
}
|
||||||
|
export async function makeObjectSerializable(obj: any, idler?: Idler): any {
|
||||||
export function makeObjectSerializable(obj: any): any {
|
|
||||||
if (!(obj instanceof Object)) {
|
if (!(obj instanceof Object)) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
const stack = [obj];
|
const stack = [obj];
|
||||||
const dict: Map<any, any> = new Map();
|
const dict: Map<any, any> = new Map();
|
||||||
while (stack.length > 0) {
|
while (stack.length > 0) {
|
||||||
|
if (idler) {
|
||||||
|
await idler.idle();
|
||||||
|
}
|
||||||
const element = stack[stack.length - 1];
|
const element = stack[stack.length - 1];
|
||||||
if (element instanceof Map) {
|
if (element instanceof Map) {
|
||||||
const {childNeedsIteration, outputArray} = processMapElement(
|
const {childNeedsIteration, outputArray} = processMapElement(
|
||||||
|
|||||||
Reference in New Issue
Block a user