Skip to content

Commit 2d97224

Browse files
authored
ENG-1490 Fix ZodError in getAllDiscourseNodes for legacy block prop shapes (#856)
* ENG-1490 Fix ZodError in getAllDiscourseNodes for legacy block prop shapes Migrate legacy discourse node block props on read instead of dropping nodes that fail schema validation. Handles two known shape changes: - specification: Condition[] → {enabled, query: {conditions, ...}} - suggestiveRules.isFirstChild: {uid, value} → boolean On successful migration, writes the fixed data back so subsequent reads hit the happy path. Nodes that can't be migrated still report to PostHog. * ENG-1490 Report post-migration error instead of pre-migration error When migration fails, report retryResult.error (remaining issues) instead of result.error (original issues) so PostHog shows actionable diagnostics. * ENG-1490 Use correct returnNode default in migration Set returnNode to "node" (matching DEFAULT_RETURN_NODE and IndexSchema default) instead of "" which bypasses Zod's .default() and would persist incorrect empty values for migrated nodes.
1 parent 902ee22 commit 2d97224

1 file changed

Lines changed: 79 additions & 11 deletions

File tree

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

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,67 @@ export const setDiscourseNodeSetting = (
493493
setBlockPropAtPath(pageUid, keys, value);
494494
};
495495

496+
/**
497+
* Migrate known legacy block prop shapes to the current schema.
498+
*
499+
* - specification: Condition[] → {enabled, query: {conditions, ...}}
500+
* - specification: {enabled, query: Condition[]} → {enabled, query: {conditions, ...}}
501+
* - suggestiveRules.isFirstChild: {uid, value} → boolean
502+
*/
503+
const migrateNodeBlockProps = (
504+
props: Record<string, json>,
505+
): Record<string, json> => {
506+
const migrated = { ...props };
507+
508+
if (Array.isArray(migrated.specification)) {
509+
migrated.specification = {
510+
enabled: migrated.specification.length > 0,
511+
query: {
512+
conditions: migrated.specification,
513+
selections: [],
514+
custom: "",
515+
returnNode: "node",
516+
},
517+
};
518+
} else if (
519+
typeof migrated.specification === "object" &&
520+
migrated.specification !== null &&
521+
"query" in migrated.specification &&
522+
Array.isArray((migrated.specification as Record<string, json>).query)
523+
) {
524+
const spec = migrated.specification as Record<string, json>;
525+
migrated.specification = {
526+
enabled:
527+
typeof spec.enabled === "boolean"
528+
? spec.enabled
529+
: (spec.query as json[]).length > 0,
530+
query: {
531+
conditions: spec.query,
532+
selections: [],
533+
custom: "",
534+
returnNode: "node",
535+
},
536+
};
537+
}
538+
539+
if (
540+
typeof migrated.suggestiveRules === "object" &&
541+
migrated.suggestiveRules !== null &&
542+
!Array.isArray(migrated.suggestiveRules)
543+
) {
544+
const rules = migrated.suggestiveRules as Record<string, json>;
545+
const ifc = rules.isFirstChild;
546+
if (typeof ifc === "object" && ifc !== null && !Array.isArray(ifc)) {
547+
migrated.suggestiveRules = {
548+
...rules,
549+
isFirstChild: (ifc as Record<string, json>).value ?? false,
550+
};
551+
}
552+
}
553+
554+
return migrated;
555+
};
556+
496557
export const getAllDiscourseNodes = (): DiscourseNodeSettings[] => {
497558
const results = window.roamAlphaAPI.data.fast.q(`
498559
[:find ?uid ?title (pull ?page [:block/props])
@@ -517,20 +578,27 @@ export const getAllDiscourseNodes = (): DiscourseNodeSettings[] => {
517578
)
518579
continue;
519580

581+
const nodeText = title.replace(DISCOURSE_NODE_PAGE_PREFIX, "");
520582
const result = DiscourseNodeSchema.safeParse(blockProps);
521583
if (result.success) {
522-
nodes.push({
523-
...result.data,
524-
type: pageUid,
525-
text: title.replace(DISCOURSE_NODE_PAGE_PREFIX, ""),
526-
});
584+
nodes.push({ ...result.data, type: pageUid, text: nodeText });
527585
} else {
528-
internalError({
529-
error: result.error,
530-
type: "DG Discourse Node Parse",
531-
context: { pageUid, title },
532-
sendEmail: false,
533-
});
586+
// Try migrating legacy field shapes before dropping the node.
587+
const migrated = migrateNodeBlockProps(
588+
blockProps as Record<string, json>,
589+
);
590+
const retryResult = DiscourseNodeSchema.safeParse(migrated);
591+
if (retryResult.success) {
592+
setBlockProps(pageUid, retryResult.data, false);
593+
nodes.push({ ...retryResult.data, type: pageUid, text: nodeText });
594+
} else {
595+
internalError({
596+
error: retryResult.error,
597+
type: "DG Discourse Node Parse",
598+
context: { pageUid, title },
599+
sendEmail: false,
600+
});
601+
}
534602
}
535603
}
536604

0 commit comments

Comments
 (0)