Skip to content

Commit 5acbf6d

Browse files
authored
eng-1135 define internalError (#608)
* eng-1135 define internalError function. This will send the stack trace to posthog, send an email, and optionally present a toast to the user. * ENG-1149 add try / catch to migration when querying for relation patterns
1 parent f519acb commit 5acbf6d

11 files changed

Lines changed: 188 additions & 121 deletions

File tree

apps/roam/src/components/CreateRelationDialog.tsx

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { render as renderToast } from "roamjs-components/components/Toast";
1212
import MenuItemSelect from "roamjs-components/components/MenuItemSelect";
1313
import AutocompleteInput from "roamjs-components/components/AutocompleteInput";
1414
import getPageTitleByPageUid from "roamjs-components/queries/getPageTitleByPageUid";
15-
import sendErrorEmail from "~/utils/sendErrorEmail";
1615
import { getSetting } from "~/utils/extensionSettings";
1716
import getDiscourseRelations, {
1817
type DiscourseRelation,
@@ -22,6 +21,7 @@ import { findDiscourseNodeByTitleAndUid } from "~/utils/findDiscourseNode";
2221
import { getDiscourseNodeFormatInnerExpression } from "~/utils/getDiscourseNodeFormatExpression";
2322
import type { DiscourseNode } from "~/utils/getDiscourseNodes";
2423
import type { Result } from "~/utils/types";
24+
import internalError from "~/utils/internalError";
2525
import getDiscourseNodes from "~/utils/getDiscourseNodes";
2626

2727
export type CreateRelationDialogProps = {
@@ -39,15 +39,6 @@ type ExtendedCreateRelationDialogProps = CreateRelationDialogProps & {
3939
selectedSourceType: DiscourseNode;
4040
};
4141

42-
const internalError = (msg: string) => {
43-
process.env.NODE_ENV === "development"
44-
? console.error(msg)
45-
: void sendErrorEmail({
46-
error: new Error(msg),
47-
type: "Create Relation Dialog Failed",
48-
}).catch(() => {});
49-
};
50-
5142
const CreateRelationDialog = ({
5243
onClose,
5344
sourceNodeUid,
@@ -112,7 +103,10 @@ const CreateRelationDialog = ({
112103
});
113104
if (selectedTargetType === false) {
114105
// should not happen at this point, since the pattern was vetted at input.
115-
internalError("Could not find identify node downstream");
106+
internalError({
107+
type: "Create Relation dialog",
108+
error: "Could not identify node downstream",
109+
});
116110
return null;
117111
}
118112
const candidateRelations = relDataByTag[selectedRelationName].filter(
@@ -132,14 +126,18 @@ const CreateRelationDialog = ({
132126
);
133127
if (candidateRelations.length === 0) {
134128
// also should not happen
135-
internalError("Could not find the relation");
129+
internalError({
130+
type: "Create Relation dialog",
131+
error: "Could not find the relation",
132+
});
136133
return null;
137134
}
138135
if (candidateRelations.length !== 1) {
139136
// This seems to happen... I need more data.
140-
internalError(
141-
`Too many relations between ${selectedTargetType.type} and ${selectedSourceType.type}: ${candidateRelations.map((r) => r.id).join(",")}`,
142-
);
137+
internalError({
138+
type: "Create Relation dialog",
139+
error: `Too many relations between ${selectedTargetType.type} and ${selectedSourceType.type}: ${candidateRelations.map((r) => r.id).join(",")}`,
140+
});
143141
return null;
144142
}
145143
return candidateRelations[0];
@@ -288,7 +286,10 @@ const prepareRelData = (
288286
});
289287
if (!nodeSchema) {
290288
// should not happen at this point, since the pattern was vetted at input.
291-
internalError("Could not find identify node downstream");
289+
internalError({
290+
error: "Could not identify node downstream",
291+
type: "Create Relation dialog",
292+
});
292293
return [];
293294
}
294295
// note the same relation could be used in both directions

apps/roam/src/components/Export.tsx

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ import {
5454
import calcCanvasNodeSizeAndImg from "~/utils/calcCanvasNodeSizeAndImg";
5555
import { DiscourseNodeShape } from "~/components/canvas/DiscourseNodeUtil";
5656
import { MAX_WIDTH } from "~/components/canvas/Tldraw";
57-
import sendErrorEmail from "~/utils/sendErrorEmail";
57+
import internalError from "~/utils/internalError";
5858
import { getSetting, setSetting } from "~/utils/extensionSettings";
5959

6060
const ExportProgress = ({ id }: { id: string }) => {
@@ -468,13 +468,12 @@ const ExportDialog: ExportDialogComponent = ({
468468
id: "query-builder-export-success",
469469
});
470470
} catch (e) {
471-
const error = e as Error;
472-
renderToast({
473-
content: "Looks like there was an error. The team has been notified.",
474-
intent: "danger",
475-
id: "discourse-graphs-error",
471+
internalError({
472+
error: e as Error,
473+
type: "Export Dialog Failed",
474+
userMessage:
475+
"Looks like there was an error. The team has been notified.",
476476
});
477-
sendErrorEmail({ error, type: "Export Dialog Failed" }).catch(() => {});
478477
} finally {
479478
setLoading(false);
480479
onClose();
@@ -688,18 +687,22 @@ const ExportDialog: ExportDialogComponent = ({
688687
setError(`Unsupported export type: ${exportType}`);
689688
}
690689
} catch (e) {
691-
const error = e as Error;
692-
renderToast({
693-
id: "export-error",
694-
content:
690+
internalError({
691+
error: e as Error,
692+
type: "Export Dialog Failed",
693+
userMessage:
695694
"Looks like there was an error. The team has been notified.",
696-
intent: "danger",
695+
context: {
696+
activeExportType,
697+
filename,
698+
resultsCount: Array.isArray(results)
699+
? results.length
700+
: undefined,
701+
sampleUids: Array.isArray(results)
702+
? results.slice(0, 10).map((r) => r.uid)
703+
: undefined,
704+
},
697705
});
698-
sendErrorEmail({
699-
error,
700-
type: "Export Dialog Failed",
701-
context: { activeExportType, filename, results },
702-
}).catch(() => {});
703706
setDialogOpen(true);
704707
setError((e as Error).message);
705708
} finally {

apps/roam/src/components/canvas/Clipboard.tsx

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import { MAX_WIDTH } from "./Tldraw";
5757
import getBlockProps from "~/utils/getBlockProps";
5858
import setBlockProps from "~/utils/setBlockProps";
5959
import { measureCanvasNodeText } from "~/utils/measureCanvasNodeText";
60-
import sendErrorEmail from "~/utils/sendErrorEmail";
60+
import internalError from "~/utils/internalError";
6161

6262
export type ClipboardPage = {
6363
uid: string;
@@ -79,21 +79,6 @@ const ClipboardContext = createContext<ClipboardContextValue | null>(null);
7979

8080
const CLIPBOARD_PROP_KEY = "pages";
8181

82-
const toError = (error: unknown, fallbackMessage: string): Error => {
83-
if (error instanceof Error) {
84-
return error;
85-
}
86-
if (typeof error === "string") {
87-
return new Error(error);
88-
}
89-
try {
90-
const serialized = JSON.stringify(error);
91-
return new Error(serialized || fallbackMessage);
92-
} catch {
93-
return new Error(fallbackMessage);
94-
}
95-
};
96-
9782
const getOrCreateClipboardBlock = async (
9883
canvasPageTitle: string,
9984
userUid: string,
@@ -155,11 +140,13 @@ export const ClipboardProvider = ({
155140
try {
156141
const userUid = getCurrentUserUid();
157142
if (!userUid) {
158-
sendErrorEmail({
143+
internalError({
159144
error: new Error("Missing current user UID"),
160145
type: "Canvas Clipboard: Missing current user UID",
161-
context: { canvasPageTitle },
162-
}).catch(() => {});
146+
context: {
147+
canvasPageTitle,
148+
},
149+
});
163150
setIsInitialized(true);
164151
return;
165152
}
@@ -179,13 +166,12 @@ export const ClipboardProvider = ({
179166
) {
180167
setPages(storedPages as ClipboardPage[]);
181168
}
182-
} catch (e) {
183-
const normalizedError = toError(e, "Failed to initialize clipboard");
184-
sendErrorEmail({
185-
error: normalizedError,
169+
} catch (error) {
170+
internalError({
171+
error,
186172
type: "Canvas Clipboard: Failed to initialize",
187173
context: { canvasPageTitle },
188-
}).catch(() => {});
174+
});
189175
} finally {
190176
setIsInitialized(true);
191177
}
@@ -201,13 +187,12 @@ export const ClipboardProvider = ({
201187
setBlockProps(clipboardBlockUid, {
202188
[CLIPBOARD_PROP_KEY]: pages,
203189
});
204-
} catch (e) {
205-
const normalizedError = toError(e, "Failed to persist clipboard state");
206-
sendErrorEmail({
207-
error: normalizedError,
190+
} catch (error) {
191+
internalError({
192+
error,
208193
type: "Canvas Clipboard: Failed to persist state",
209194
context: { clipboardBlockUid, pageCount: pages.length },
210-
}).catch(() => {});
195+
});
211196
}
212197
}, [pages, clipboardBlockUid, isInitialized]);
213198

@@ -287,8 +272,8 @@ const AddPageModal = ({ isOpen, onClose, onConfirm }: AddPageModalProps) => {
287272
// eslint-disable-next-line @typescript-eslint/await-thenable
288273
const raw = await window.roamAlphaAPI.data.backend.q(
289274
`
290-
[:find ?text ?uid
291-
:where
275+
[:find ?text ?uid
276+
:where
292277
[?e :node/title ?text]
293278
[?e :block/uid ?uid]]`,
294279
);
@@ -447,15 +432,11 @@ const ClipboardPageSection = ({
447432
);
448433
setDiscourseNodes(nodes);
449434
} catch (error) {
450-
const normalizedError = toError(
435+
internalError({
451436
error,
452-
"Failed to fetch discourse nodes",
453-
);
454-
sendErrorEmail({
455-
error: normalizedError,
456437
type: "Canvas Clipboard: Failed to fetch discourse nodes",
457438
context: { pageTitle: page.text },
458-
}).catch(() => {});
439+
});
459440
setDiscourseNodes([]);
460441
} finally {
461442
setIsLoading(false);
@@ -610,11 +591,11 @@ const ClipboardPageSection = ({
610591

611592
const nodeType = findDiscourseNode(node.uid);
612593
if (!nodeType) {
613-
sendErrorEmail({
614-
error: new Error("Node type not found"),
594+
internalError({
595+
error: new Error("Canvas Clipboard: Node type not found"),
615596
type: "Canvas Clipboard: Node type not found",
616597
context: { uid: node.uid },
617-
}).catch(() => {});
598+
});
618599
return;
619600
}
620601

apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ import getCurrentPageUid from "roamjs-components/dom/getCurrentPageUid";
8080
import getPageTitleByPageUid from "roamjs-components/queries/getPageTitleByPageUid";
8181
import { AddReferencedNodeType } from "./DiscourseRelationTool";
8282
import { dispatchToastEvent } from "~/components/canvas/ToastListener";
83-
import sendErrorEmail from "~/utils/sendErrorEmail";
83+
import internalError from "~/utils/internalError";
8484

8585
const COLOR_ARRAY = Array.from(textShapeProps.color.values)
8686
.filter((c) => !["red", "green", "grey"].includes(c))
@@ -564,12 +564,10 @@ export const createAllRelationShapeUtils = (
564564
relationBlockUid: relation.id,
565565
});
566566
else {
567-
void sendErrorEmail({
568-
error: new Error(
569-
"attempt to create a relation between non discourse nodes",
570-
),
571-
type: "canvas-create-relation-non-dgn",
572-
}).catch(() => undefined);
567+
void internalError({
568+
error: "attempt to create a relation between non discourse nodes",
569+
type: "Canvas create relation",
570+
});
573571
}
574572
} else {
575573
const { triples, label: relationLabel } = relation;

apps/roam/src/components/canvas/Tldraw.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ import { createMigrations } from "./DiscourseRelationShape/discourseRelationMigr
8686
import ToastListener, { dispatchToastEvent } from "./ToastListener";
8787
import { CanvasDrawerPanel } from "./CanvasDrawer";
8888
import { ClipboardPanel, ClipboardProvider } from "./Clipboard";
89-
import sendErrorEmail from "~/utils/sendErrorEmail";
89+
import internalError from "~/utils/internalError";
9090
import { AUTO_CANVAS_RELATIONS_KEY } from "~/data/userSettings";
9191
import { getSetting } from "~/utils/extensionSettings";
9292
import { isPluginTimerReady, waitForPluginTimer } from "~/utils/pluginTimer";
@@ -516,16 +516,14 @@ const TldrawCanvas = ({ title }: { title: string }) => {
516516
error.stack = e.detail.stack;
517517
}
518518

519-
sendErrorEmail({
519+
internalError({
520520
error,
521521
type: "Tldraw Error",
522522
context: {
523523
title: title,
524524
lastActions: lastActionsRef.current,
525525
},
526-
}).catch(() => {});
527-
528-
console.error("Tldraw Error:", e.detail);
526+
});
529527
};
530528

531529
document.addEventListener(

apps/roam/src/components/canvas/useRoamStore.ts

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ import {
2525
} from "tldraw";
2626
import { AddPullWatch } from "roamjs-components/types";
2727
import { LEGACY_SCHEMA } from "~/data/legacyTldrawSchema";
28-
import sendErrorEmail from "~/utils/sendErrorEmail";
29-
import getCurrentUserDisplayName from "roamjs-components/queries/getCurrentUserDisplayName";
28+
import internalError from "~/utils/internalError";
3029

3130
const THROTTLE = 350;
3231

@@ -137,26 +136,25 @@ export const useRoamStore = ({
137136

138137
const handleStoreError = ({
139138
error,
140-
errorMessage,
139+
type,
141140
}: {
142141
error: Error;
143-
errorMessage: string;
142+
type: string;
144143
}): void => {
145-
console.error(errorMessage, error);
146144
setError(error);
147145
setLoading(false);
148146
const snapshotSize = initialSnapshot
149147
? JSON.stringify(initialSnapshot).length
150148
: 0;
151-
sendErrorEmail({
149+
internalError({
152150
error,
153-
type: errorMessage,
151+
type,
154152
context: {
155153
pageUid,
156154
snapshotSize,
157155
...(snapshotSize < 10000 ? { initialSnapshot } : {}),
158156
},
159-
}).catch(() => {});
157+
});
160158
};
161159

162160
// eslint-disable-next-line @typescript-eslint/naming-convention
@@ -171,7 +169,7 @@ export const useRoamStore = ({
171169
} catch (e) {
172170
handleStoreError({
173171
error: e as Error,
174-
errorMessage: "Failed to create TLStore",
172+
type: "Failed to create TLStore",
175173
});
176174
return null;
177175
}
@@ -182,7 +180,7 @@ export const useRoamStore = ({
182180
} catch (e) {
183181
handleStoreError({
184182
error: e as Error,
185-
errorMessage: "Failed to migrate snapshot",
183+
type: "Failed to migrate snapshot",
186184
});
187185
return null;
188186
}
@@ -305,14 +303,13 @@ export const useRoamStore = ({
305303
setNeedsUpgrade(false);
306304
setInitialSnapshot(null);
307305
setError(error);
308-
sendErrorEmail({
306+
internalError({
309307
error,
310308
type: "Failed to perform Canvas upgrade",
311309
context: {
312310
data: { oldData },
313311
},
314-
}).catch(() => {});
315-
console.error("Failed to perform Canvas upgrade", error);
312+
});
316313
}
317314
};
318315

0 commit comments

Comments
 (0)