Skip to content

Commit 14e72c9

Browse files
committed
Add migration telemetry for legacy parse failures
1 parent b97def3 commit 14e72c9

1 file changed

Lines changed: 74 additions & 16 deletions

File tree

apps/roam/src/components/settings/utils/migrateLegacyToBlockProps.ts

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import getBlockUidByTextOnPage from "roamjs-components/queries/getBlockUidByText
55
import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle";
66
import { createBlock } from "roamjs-components/writes";
77
import { getSetting, setSetting } from "~/utils/extensionSettings";
8+
import internalError from "~/utils/internalError";
89
import {
910
readAllLegacyFeatureFlags,
1011
readAllLegacyGlobalSettings,
@@ -26,6 +27,7 @@ import type { z } from "zod";
2627
const LOG_PREFIX = "[DG BlockProps Migration]";
2728
const GRAPH_MIGRATION_MARKER = "Block props migrated";
2829
const PERSONAL_MIGRATION_MARKER = "dg-personal-settings-migrated";
30+
const MAX_ERROR_CONTEXT_LENGTH = 5000;
2931

3032
const hasGraphMigrationMarker = (): boolean =>
3133
!!getBlockUidByTextOnPage({
@@ -39,6 +41,14 @@ const isPropsValid = (
3941
): boolean =>
4042
!!props && Object.keys(props).length > 0 && schema.safeParse(props).success;
4143

44+
const serializeErrorContext = (value: unknown): string => {
45+
try {
46+
return JSON.stringify(value).slice(0, MAX_ERROR_CONTEXT_LENGTH);
47+
} catch {
48+
return String(value);
49+
}
50+
};
51+
4252
const shouldWrite = (
4353
schema: z.ZodTypeAny,
4454
currentProps: Record<string, json> | null,
@@ -48,11 +58,6 @@ const shouldWrite = (
4858
return true;
4959
}
5060

51-
// Compare Zod-normalized parsed legacy against current props directly.
52-
// Safe on retry: if prior run already wrote parsedLegacy, they'll match
53-
// → skip. If user edited via settings UI, dual-write keeps both sides in
54-
// sync → match → skip. The only write happens when legacy genuinely
55-
// differs from current (first migration or tree-only edit).
5661
return JSON.stringify(parsedLegacy) !== JSON.stringify(currentProps);
5762
};
5863

@@ -71,9 +76,6 @@ const migrateSection = ({
7176

7277
const parseResult = schema.safeParse(legacyData);
7378
if (!parseResult.success) {
74-
// Legacy malformed — succeed if current props are already valid.
75-
// migrateGraphLevel runs before initDiscourseNodePages, so valid props
76-
// at this point were written by a prior migration run, not init-seeded.
7779
if (isPropsValid(schema, currentProps)) {
7880
console.log(
7981
`${LOG_PREFIX} ${label}: legacy malformed but props already valid, skipping`,
@@ -83,6 +85,17 @@ const migrateSection = ({
8385
console.warn(`${LOG_PREFIX} ${label}: Zod validation failed, skipping`, {
8486
error: parseResult.error.message,
8587
});
88+
internalError({
89+
error: parseResult.error,
90+
type: "DG Block Props Migration",
91+
context: {
92+
label,
93+
blockUid,
94+
legacyData: serializeErrorContext(legacyData),
95+
currentProps: serializeErrorContext(currentProps),
96+
},
97+
sendEmail: false,
98+
});
8699
return false;
87100
}
88101

@@ -117,8 +130,6 @@ const migrateDiscourseNodes = (): boolean => {
117130
nodeText,
118131
);
119132
if (!legacyData) {
120-
// Legacy unreadable — if current props are already valid, treat as
121-
// success so a missing/malformed legacy tree doesn't block the marker.
122133
if (isPropsValid(DiscourseNodeSchema, getBlockProps(nodePageUid))) {
123134
console.log(
124135
`${LOG_PREFIX} Discourse Node (${nodeText}): legacy unreadable but props already valid, skipping`,
@@ -128,6 +139,16 @@ const migrateDiscourseNodes = (): boolean => {
128139
console.warn(
129140
`${LOG_PREFIX} Discourse Node (${nodeText}): legacy data unreadable`,
130141
);
142+
internalError({
143+
error: `Legacy discourse node data unreadable: ${nodeText}`,
144+
type: "DG Block Props Migration",
145+
context: {
146+
label: `Discourse Node (${nodeText})`,
147+
blockUid: nodePageUid,
148+
currentProps: serializeErrorContext(getBlockProps(nodePageUid)),
149+
},
150+
sendEmail: false,
151+
});
131152
allOk = false;
132153
continue;
133154
}
@@ -151,7 +172,15 @@ export const migrateGraphLevel = async (
151172
blockUids: Record<string, string>,
152173
): Promise<void> => {
153174
const pageUid = getPageUidByPageTitle(DG_BLOCK_PROP_SETTINGS_PAGE_TITLE);
154-
if (!pageUid) return;
175+
if (!pageUid) {
176+
internalError({
177+
error: `Settings page not found: ${DG_BLOCK_PROP_SETTINGS_PAGE_TITLE}`,
178+
type: "DG Block Props Migration",
179+
context: { scope: "graph" },
180+
sendEmail: false,
181+
});
182+
return;
183+
}
155184

156185
if (hasGraphMigrationMarker()) {
157186
console.log(`${LOG_PREFIX} graph-level: skipped (already migrated)`);
@@ -160,9 +189,19 @@ export const migrateGraphLevel = async (
160189

161190
let failures = 0;
162191

163-
// Feature flags
164192
const featureFlagUid = blockUids[TOP_LEVEL_BLOCK_PROP_KEYS.featureFlags];
165-
if (featureFlagUid) {
193+
if (!featureFlagUid) {
194+
internalError({
195+
error: `Missing block uid for ${TOP_LEVEL_BLOCK_PROP_KEYS.featureFlags}`,
196+
type: "DG Block Props Migration",
197+
context: {
198+
scope: "graph",
199+
blockUids: serializeErrorContext(blockUids),
200+
},
201+
sendEmail: false,
202+
});
203+
failures++;
204+
} else {
166205
const legacyFlags = readAllLegacyFeatureFlags();
167206
if (
168207
!migrateSection({
@@ -176,9 +215,19 @@ export const migrateGraphLevel = async (
176215
}
177216
}
178217

179-
// Global settings
180218
const globalUid = blockUids[TOP_LEVEL_BLOCK_PROP_KEYS.global];
181-
if (globalUid) {
219+
if (!globalUid) {
220+
internalError({
221+
error: `Missing block uid for ${TOP_LEVEL_BLOCK_PROP_KEYS.global}`,
222+
type: "DG Block Props Migration",
223+
context: {
224+
scope: "graph",
225+
blockUids: serializeErrorContext(blockUids),
226+
},
227+
sendEmail: false,
228+
});
229+
failures++;
230+
} else {
182231
const legacyGlobal = readAllLegacyGlobalSettings();
183232
if (
184233
!migrateSection({
@@ -192,7 +241,6 @@ export const migrateGraphLevel = async (
192241
}
193242
}
194243

195-
// Discourse nodes
196244
if (!migrateDiscourseNodes()) {
197245
failures++;
198246
}
@@ -231,6 +279,16 @@ export const migratePersonalSettings = async (
231279
console.warn(
232280
`${LOG_PREFIX} personal: block not found for key "${personalKey}", skipping`,
233281
);
282+
internalError({
283+
error: `Missing personal settings block for key "${personalKey}"`,
284+
type: "DG Block Props Migration",
285+
context: {
286+
scope: "personal",
287+
personalKey,
288+
blockUids: serializeErrorContext(blockUids),
289+
},
290+
sendEmail: false,
291+
});
234292
return;
235293
}
236294

0 commit comments

Comments
 (0)