Compare commits

..

10 Commits

Author SHA1 Message Date
codegen-sh[bot] cd2737d84f Fix TypeScript error: Add type assertion for filtered parsedPages 2025-04-02 23:10:29 +00:00
Tom Moor 193dfb04db Change to trigger CI 2025-04-02 15:38:18 -07:00
codegen-sh[bot] 0192af53f2 Applied automatic fixes 2025-04-02 00:48:29 +00:00
Codegen 1d63cae632 Fix: Implement additional improvements for Notion import error handling 2025-04-02 00:45:27 +00:00
Tom Moor e279dde0b4 Touch to trigger actions 2025-04-01 04:40:37 -07:00
codegen-sh[bot] 487999a19e Applied automatic fixes 2025-03-31 21:44:52 +00:00
codegen-sh[bot] 6bb631dedf Fix: Use Logger.warn instead of console.log in Notion import task 2025-03-31 21:42:04 +00:00
codegen-sh[bot] 79a9856500 Fix: Handle Notion database not found errors gracefully 2025-03-31 21:36:29 +00:00
Tom Moor 567ca7e3f1 fix: Table columns sometimes lost in copy paste (#8845)
closes #8841
2025-03-30 20:06:22 -07:00
codegen-sh[bot] 97c3ea7da8 Allow inline code to be bolded and italicized (#8843)
Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
2025-03-30 14:44:21 -07:00
4 changed files with 64 additions and 29 deletions
@@ -1,6 +1,8 @@
import { APIResponseError, APIErrorCode } from "@notionhq/client";
import { ImportTaskInput, ImportTaskOutput } from "@shared/schema";
import { IntegrationService, ProsemirrorDoc } from "@shared/types";
import { ProsemirrorHelper } from "@shared/utils/ProsemirrorHelper";
import Logger from "@server/logging/Logger";
import { Integration } from "@server/models";
import ImportTask from "@server/models/ImportTask";
import APIImportTask, {
@@ -39,7 +41,10 @@ export default class NotionAPIImportTask extends APIImportTask<IntegrationServic
importTask.input.map(async (item) => this.processPage({ item, client }))
);
const taskOutput: ImportTaskOutput = parsedPages.map((parsedPage) => ({
// Filter out any null results (from pages/databases that couldn't be accessed)
const validParsedPages = parsedPages.filter(Boolean) as ParsePageOutput[];
const taskOutput: ImportTaskOutput = validParsedPages.map((parsedPage) => ({
externalId: parsedPage.externalId,
title: parsedPage.title,
emoji: parsedPage.emoji,
@@ -50,7 +55,7 @@ export default class NotionAPIImportTask extends APIImportTask<IntegrationServic
}));
const childTasksInput: ImportTaskInput<IntegrationService.Notion> =
parsedPages.flatMap((parsedPage) =>
validParsedPages.flatMap((parsedPage) =>
parsedPage.children.map((childPage) => ({
type: childPage.type,
externalId: childPage.externalId,
@@ -88,36 +93,55 @@ export default class NotionAPIImportTask extends APIImportTask<IntegrationServic
}: {
item: ImportTaskInput<IntegrationService.Notion>[number];
client: NotionClient;
}): Promise<ParsePageOutput> {
}): Promise<ParsePageOutput | null> {
const collectionExternalId = item.collectionExternalId ?? item.externalId;
// Convert Notion database to an empty page with "pages in database" as its children.
if (item.type === PageType.Database) {
const { pages, ...databaseInfo } = await client.fetchDatabase(
item.externalId
);
try {
// Convert Notion database to an empty page with "pages in database" as its children.
if (item.type === PageType.Database) {
const { pages, ...databaseInfo } = await client.fetchDatabase(
item.externalId
);
return {
...databaseInfo,
externalId: item.externalId,
content: ProsemirrorHelper.getEmptyDocument() as ProsemirrorDoc,
collectionExternalId,
children: pages.map((page) => ({
type: page.type,
externalId: page.id,
})),
};
}
const { blocks, ...pageInfo } = await client.fetchPage(item.externalId);
return {
...databaseInfo,
...pageInfo,
externalId: item.externalId,
content: ProsemirrorHelper.getEmptyDocument() as ProsemirrorDoc,
content: NotionConverter.page({ children: blocks } as NotionPage),
collectionExternalId,
children: pages.map((page) => ({
type: page.type,
externalId: page.id,
})),
children: this.parseChildPages(blocks),
};
} catch (error) {
if (error instanceof APIResponseError) {
// Skip this page/database if it's not found or not accessible
if (
error.code === APIErrorCode.ObjectNotFound ||
error.code === APIErrorCode.Unauthorized
) {
Logger.warn(
`Skipping Notion ${
item.type === PageType.Database ? "database" : "page"
} ${item.externalId} - Error code: ${error.code} - ${error.message}`
);
return null;
}
}
// Re-throw other errors to be handled by the parent try/catch
throw error;
}
const { blocks, ...pageInfo } = await client.fetchPage(item.externalId);
return {
...pageInfo,
externalId: item.externalId,
content: NotionConverter.page({ children: blocks } as NotionPage),
collectionExternalId,
children: this.parseChildPages(blocks),
};
}
/**
+10 -1
View File
@@ -304,6 +304,15 @@ export default abstract class ImportsProcessor<
const output = outputMap[externalId];
// Skip this item if it has no output (likely due to an error during processing)
if (!output) {
Logger.debug(
"processor",
`Skipping item with no output: ${externalId}`
);
continue;
}
const collectionItem = importInput[externalId];
const attachments = await Attachment.findAll({
@@ -444,7 +453,7 @@ export default abstract class ImportsProcessor<
importInput: Record<string, ImportInput<any>[number]>;
actorId: string;
}): ProsemirrorDoc {
// special case when the doc content is empty
// special case when the doc content is empty.
if (!content.content.length) {
return content;
}
+1 -1
View File
@@ -170,7 +170,7 @@ export default abstract class APIImportTask<
await importTask.save({ transaction });
const associatedImport = importTask.import;
associatedImport.documentCount += importTask.input.length;
associatedImport.documentCount += taskOutputWithReplacements.length;
await associatedImport.saveWithCtx(
createContext({
user: associatedImport.createdBy,
+5 -3
View File
@@ -28,9 +28,10 @@ export function getCellAttrs(dom: HTMLElement | string): Attrs {
const widthAttr = dom.getAttribute("data-colwidth");
const widths =
widthAttr && /^\d+(,\d+)*$/.test(widthAttr)
? widthAttr.split(",").map((s) => Number(s))
? widthAttr.split(",").map(Number)
: null;
const colspan = Number(dom.getAttribute("colspan") || 1);
return {
colspan,
rowspan: Number(dom.getAttribute("rowspan") || 1),
@@ -63,10 +64,11 @@ export function setCellAttrs(node: Node): Attrs {
}
if (node.attrs.colwidth) {
if (isBrowser) {
attrs["data-colwidth"] = node.attrs.colwidth.join(",");
attrs["data-colwidth"] = node.attrs.colwidth.map(parseInt).join(",");
} else {
attrs.style =
(attrs.style ?? "") + `min-width: ${node.attrs.colwidth}px;`;
(attrs.style ?? "") +
`min-width: ${parseInt(node.attrs.colwidth[0])}px;`;
}
}