diff --git a/desktop/app/src/__mocks__/uuid.tsx b/desktop/app/src/__mocks__/uuid.tsx new file mode 100644 index 000000000..0f418648c --- /dev/null +++ b/desktop/app/src/__mocks__/uuid.tsx @@ -0,0 +1,16 @@ +/** + * 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 + */ + +export function v4() { + return '00000000-0000-0000-0000-000000000000'; +} + +export function v1() { + return '00000000-0000-0000-0000-000000000000'; +} diff --git a/desktop/app/src/fb-stubs/ErrorReporter.tsx b/desktop/app/src/fb-stubs/ErrorReporter.tsx index 7829e7abd..53a63d626 100644 --- a/desktop/app/src/fb-stubs/ErrorReporter.tsx +++ b/desktop/app/src/fb-stubs/ErrorReporter.tsx @@ -15,14 +15,7 @@ export function cleanStack(_stack: string, _loc?: string) {} import ScribeLogger from './ScribeLogger'; -export type ObjectError = - | Error - | { - message: string; - stack?: string; - }; - export default class ErrorReporter { constructor(_scribeLogger: ScribeLogger) {} - report(_err: ObjectError) {} + report(_err: Error) {} } diff --git a/desktop/app/src/utils/errors.tsx b/desktop/app/src/utils/errors.tsx index 1ea305e4f..28eb5cd07 100644 --- a/desktop/app/src/utils/errors.tsx +++ b/desktop/app/src/utils/errors.tsx @@ -7,11 +7,20 @@ * @format */ +import {InteractionReport} from 'flipper-plugin'; + export class CancelledPromiseError extends Error { constructor(msg: string) { super(msg); this.name = 'CancelledPromiseError'; } + name: 'CancelledPromiseError'; +} + +declare global { + interface Error { + interaction?: InteractionReport; + } } export function isError(obj: any): obj is Error { diff --git a/desktop/flipper-plugin/package.json b/desktop/flipper-plugin/package.json index f4d536784..aabd662e4 100644 --- a/desktop/flipper-plugin/package.json +++ b/desktop/flipper-plugin/package.json @@ -12,12 +12,14 @@ "@emotion/css": "^11.1.3", "@emotion/react": "^11.4.0", "@reach/observe-rect": "^1.2.0", + "@types/uuid": "^8.3.0", "immer": "^9.0.2", "lodash": "^4.17.21", "react-color": "^2.19.3", "react-element-to-jsx-string": "^14.3.2", "react-virtual": "^2.7.1", - "string-natural-compare": "^3.0.0" + "string-natural-compare": "^3.0.0", + "uuid": "^8.3.2" }, "devDependencies": { "@types/jest": "^26.0.23", diff --git a/desktop/flipper-plugin/src/__tests__/api.node.tsx b/desktop/flipper-plugin/src/__tests__/api.node.tsx index c2c663370..73edc900f 100644 --- a/desktop/flipper-plugin/src/__tests__/api.node.tsx +++ b/desktop/flipper-plugin/src/__tests__/api.node.tsx @@ -92,6 +92,8 @@ test('Correct top level API exposed', () => { "FlipperLib", "HighlightManager", "Idler", + "InteractionReport", + "InteractionReporter", "LogLevel", "LogTypes", "Logger", diff --git a/desktop/flipper-plugin/src/index.ts b/desktop/flipper-plugin/src/index.ts index 3215bae9f..31e956e68 100644 --- a/desktop/flipper-plugin/src/index.ts +++ b/desktop/flipper-plugin/src/index.ts @@ -69,6 +69,8 @@ export { withTrackingScope, useTrackedCallback, wrapInteractionHandler as _wrapInteractionHandler, + InteractionReport, + InteractionReporter, } from './ui/Tracked'; export {DataFormatter} from './ui/DataFormatter'; diff --git a/desktop/flipper-plugin/src/ui/Tracked.tsx b/desktop/flipper-plugin/src/ui/Tracked.tsx index 2dcf76e35..b1cf87b34 100644 --- a/desktop/flipper-plugin/src/ui/Tracked.tsx +++ b/desktop/flipper-plugin/src/ui/Tracked.tsx @@ -10,6 +10,7 @@ import React, {useMemo} from 'react'; import {Children, cloneElement, createContext, useContext} from 'react'; import reactElementToJSXString from 'react-element-to-jsx-string'; +import {v4 as uuid} from 'uuid'; export type InteractionReport = { // Duration of the event handler itself, not including any time the promise handler might have been pending @@ -22,6 +23,7 @@ export type InteractionReport = { action: string; componentType: string; event: string; + uuid: string; }; export type InteractionReporter = (report: InteractionReport) => void; @@ -124,8 +126,9 @@ export function wrapInteractionHandler( scope: string, action?: string, ): T { + const interaction_uuid = uuid(); function report(start: number, initialEnd: number, error?: any) { - globalInteractionReporter({ + const interactionReport: InteractionReport = { duration: initialEnd - start, totalDuration: Date.now() - start, success: error ? 0 : 1, @@ -143,7 +146,13 @@ export function wrapInteractionHandler( : 'unknown'), scope, event, - }); + uuid: interaction_uuid, + }; + if (error && typeof error === 'object') { + // associate the error with the interaction caused it + error.interaction = interactionReport; + } + globalInteractionReporter(interactionReport); } const res = function trappedInteractionHandler(this: any) { @@ -168,7 +177,7 @@ export function wrapInteractionHandler( (error: any) => r(initialEnd, error), ); res = res.catch((error: any) => { - // we need to create another rejected promise so error is again marked as "unhandled" + // we need to create another rejected promise so error is again marked as "unhandled". return Promise.reject(error); }); } else { diff --git a/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx b/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx index 9c5e69b83..663ff9065 100644 --- a/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx +++ b/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx @@ -54,6 +54,7 @@ test('Tracked button', () => { event: 'onClick', scope: 'Flipper', success: 1, + uuid: '00000000-0000-0000-0000-000000000000', }); }); @@ -74,6 +75,7 @@ test('Tracked button - custom handler', () => { event: 'onDoubleClick', scope: 'Flipper', success: 1, + uuid: '00000000-0000-0000-0000-000000000000', }); }); @@ -99,6 +101,7 @@ test('Throwing action', () => { event: 'click', scope: 'test', success: 0, + uuid: '00000000-0000-0000-0000-000000000000', }); }); @@ -124,6 +127,7 @@ test('Async action', async () => { event: 'click', scope: 'test', success: 1, + uuid: '00000000-0000-0000-0000-000000000000', }); }); @@ -155,6 +159,7 @@ test('Throwing async action', async () => { event: 'click', scope: 'test', success: 0, + uuid: '00000000-0000-0000-0000-000000000000', }); }); diff --git a/desktop/yarn.lock b/desktop/yarn.lock index d7691cea5..e16736821 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -3143,7 +3143,7 @@ resolved "https://registry.yarnpkg.com/@types/url-join/-/url-join-4.0.0.tgz#72eff71648a429c7d4acf94e03780e06671369bd" integrity sha512-awrJu8yML4E/xTwr2EMatC+HBnHGoDxc2+ImA9QyeUELI1S7dOCIZcyjki1rkwoA8P2D2NVgLAJLjnclkdLtAw== -"@types/uuid@^8.0.0", "@types/uuid@^8.0.1": +"@types/uuid@^8.0.0", "@types/uuid@^8.0.1", "@types/uuid@^8.3.0": version "8.3.0" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f" integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==