Format non-errors in export panel

Summary: We catch everything that's thrown during the export, which sadly doesn't guarantee that everything is actually an `Error`. So we'll try to JSONify it instead if that happens. It's gross, but well, JavaScript.

Reviewed By: danielbuechele

Differential Revision: D15943111

fbshipit-source-id: 03e6c18403961c00c5878d093c813573461e637b
This commit is contained in:
Pascal Hartig
2019-06-24 04:53:05 -07:00
committed by Facebook Github Bot
parent 157f5a4ea6
commit 136eca41ad
4 changed files with 109 additions and 53 deletions

View File

@@ -22,6 +22,7 @@ import {shareFlipperData} from '../fb-stubs/user';
import {exportStore, EXPORT_FLIPPER_TRACE_EVENT} from '../utils/exportData.js';
import PropTypes from 'prop-types';
import {clipboard} from 'electron';
import ShareSheetErrorList from './ShareSheetErrorList.js';
import {reportPlatformFailures} from '../utils/metrics';
// $FlowFixMe: Missing type defs for node built-in.
import {performance} from 'perf_hooks';
@@ -42,36 +43,27 @@ const Uploading = styled(Text)({
marginTop: 15,
});
const ErrorMessage = styled(Text)({
display: 'block',
marginTop: 6,
wordBreak: 'break-all',
whiteSpace: 'pre-line',
lineHeight: 1.35,
});
const Copy = styled(Input)({
marginRight: 0,
marginBottom: 15,
});
const Title = styled(Text)({
marginBottom: 6,
});
const InfoText = styled(Text)({
lineHeight: 1.35,
marginBottom: 15,
});
const Padder = styled('div')(
({paddingLeft, paddingRight, paddingBottom, paddingTop}) => ({
paddingLeft: paddingLeft || 0,
paddingRight: paddingRight || 0,
paddingBottom: paddingBottom || 0,
paddingTop: paddingTop || 0,
}),
);
const Title = styled(Text)({
marginBottom: 6,
});
const ErrorMessage = styled(Text)({
display: 'block',
marginTop: 6,
wordBreak: 'break-all',
whiteSpace: 'pre-line',
lineHeight: 1.35,
});
type Props = {
onHide: () => mixed,
@@ -153,20 +145,7 @@ export default class ShareSheet extends Component<Props, State> {
data might contain sensitve information like access tokens
used in network requests.
</InfoText>
{this.state.errorArray.length > 0 && (
<Padder paddingBottom={8}>
<FlexColumn>
<Title bold>
The following errors occurred while exporting your
data
</Title>
{this.state.errorArray.map((e: Error) => {
return (
<ErrorMessage code>{e.toString()}</ErrorMessage>
);
})}
</FlexColumn>
</Padder>
<ShareSheetErrorList errors={this.state.errorArray} />
)}
</>
) : (

View File

@@ -0,0 +1,69 @@
/**
* 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 {PureComponent, FlexColumn, Text, styled} from 'flipper';
type Props = {|
errors: Array<Error>,
|};
const ErrorMessage = styled(Text)({
display: 'block',
marginTop: 6,
wordBreak: 'break-all',
whiteSpace: 'pre-line',
lineHeight: 1.35,
});
const Padder = styled('div')(
({paddingLeft, paddingRight, paddingBottom, paddingTop}) => ({
paddingLeft: paddingLeft || 0,
paddingRight: paddingRight || 0,
paddingBottom: paddingBottom || 0,
paddingTop: paddingTop || 0,
}),
);
const Title = styled(Text)({
marginBottom: 6,
});
export function formatError(e: Error): string {
const estr = e.toString();
if (estr === '[object Object]') {
try {
return JSON.stringify(e);
} catch (e) {
return '<unrepresentable error>';
}
}
return estr;
}
export default class Popover extends PureComponent<Props> {
render() {
if (this.props.errors.length === 0) {
return null;
}
return (
<>
<Padder paddingBottom={8}>
<FlexColumn>
<Title bold>
The following errors occurred while exporting your data
</Title>
{this.props.errors.map((e: Error) => (
<ErrorMessage code>{formatError(e)}</ErrorMessage>
))}
</FlexColumn>
</Padder>
</>
);
}
}

View File

@@ -24,6 +24,7 @@ import {
EXPORT_FLIPPER_TRACE_EVENT,
} from '../utils/exportData.js';
import PropTypes from 'prop-types';
import ShareSheetErrorList from './ShareSheetErrorList.js';
const Container = styled(FlexColumn)({
padding: 20,
@@ -57,20 +58,12 @@ const InfoText = styled(Text)({
marginBottom: 15,
});
const Padder = styled('div')(
({paddingLeft, paddingRight, paddingBottom, paddingTop}) => ({
paddingLeft: paddingLeft || 0,
paddingRight: paddingRight || 0,
paddingBottom: paddingBottom || 0,
paddingTop: paddingTop || 0,
}),
);
type Props = {
onHide: () => mixed,
file: ?string,
logger: Logger,
};
type State = {
errorArray: Array<Error>,
result: ?{
@@ -128,16 +121,7 @@ export default class ShareSheetExportFile extends Component<Props, State> {
might contain sensitive information like access tokens used in
network requests.
</InfoText>
{this.state.errorArray.length > 0 && (
<Padder paddingBottom={8}>
<FlexColumn>
<Title bold>Errors: </Title>
{this.state.errorArray.map((e: Error) => {
return <ErrorMessage code>{e.toString()}</ErrorMessage>;
})}
</FlexColumn>
</Padder>
)}
<ShareSheetErrorList errors={this.state.errorArray} />
</FlexColumn>
<FlexRow>
<Spacer />

View 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 {formatError} from '../ShareSheetErrorList.js';
test('normal error is formatted', () => {
const e = new Error('something went wrong');
expect(formatError(e)).toEqual('Error: something went wrong');
});
test('objects are formatted', () => {
const e: any = {iam: 'not an error'};
expect(formatError(e)).toEqual('{"iam":"not an error"}');
});
test('recursive data structures are not formatted', () => {
const e: any = {b: null};
e.b = e;
expect(formatError(e)).toEqual('<unrepresentable error>');
});