-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
API importer for Notion #8710
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
API importer for Notion #8710
Conversation
* tom/misc-fixes (#8650) * Revert "chore: Upgrade `path-to-regexp` (#8636)" (#8652) This reverts commit 58c4a48. * chore(deps): bump axios from 1.7.9 to 1.8.2 (#8653) Bumps [axios](https://github.com/axios/axios) from 1.7.9 to 1.8.2. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](axios/axios@v1.7.9...v1.8.2) --- updated-dependencies: - dependency-name: axios dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: Bump dd-trace (#8654) * chore: Bump @koa/bull-board (#8655) * create import_tasks table * rate-limited notion api calls * completion flow * ImportsProcessor create event handling * update imports table migration file * works till final step * page title and emoji * works almost - tiny bugs in persistence * remove local oauth redirect * stash * Passing test suite * fix: Nested lists * Add video node * fix: Missing paragraphs * Add more mention support * works with attachments and mentions * better types * page type from ui * wip: database support * list consolidated imports * websocket events * name param in import dialog * import policy * handle failures * abstraction and cleanup * even more abstraction and typings * attachments logic to base api-import task * delete import and policies * indexes and scopes * notion database support * order * fix: Filter empty rich_text * Add callout support * Add column, column_list support (flatted into document until we have support) * Add link_preview * Add synced_block (flatten into page) * refactor persistence * generate import name * fix issues; don't use documentCreator as it is lossy based on text * remove local oauth redirect * remove commented code --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Tom Moor <tom@getoutline.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tom Moor <tom.moor@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces an API importer for Notion, adding both backend API routes and frontend integration components to enable importing Notion pages into the application. Key changes include:
- New API endpoints and OAuth handling for Notion (plugins/notion/server/api/notion.ts and schema).
- Frontend integration for initiating and managing Notion imports via a new dialog and import menu.
- Additional store, WebSocket event listeners, and plugin hook support for managing import-related functionality.
Reviewed Changes
Copilot reviewed 66 out of 66 changed files in this pull request and generated no comments.
Show a summary per file
File | Description |
---|---|
plugins/notion/server/api/notion.ts | Implements Notion callback and search endpoints. |
plugins/notion/client/Imports.tsx | Provides the Notion import button and OAuth flow initiation. |
plugins/notion/server/env.ts | Configures the Notion client credentials. |
app/models/Import.ts | Introduces a new Import model to track import state. |
plugins/notion/client/index.tsx | Registers the Notion import UI component via plugin hooks. |
app/menus/ImportMenu.tsx | Adds a context menu for import items with delete actions. |
app/scenes/Settings/components/ImportListItem.tsx | Displays import details including status and page counts. |
app/stores/ImportsStore.ts | Provides a dedicated store for managing Import models. |
plugins/notion/server/api/schema.ts | Defines schemas for Notion callback and search requests. |
plugins/notion/client/components/ImportDialog.tsx | Implements a dialog to select pages and set permissions for import. |
app/scenes/Settings/Import.ts | Updates the import scene to integrate the new Notion import flow. |
app/components/WebsocketProvider.ts | Adds event listeners for real-time import updates. |
app/utils/PluginManager.ts | Introduces a new plugin hook for imports. |
app/components/Dialogs.tsx | Improves dialog onClose handling. |
app/hooks/usePolicy.ts | Adds an option to force reload policy data. |
app/stores/DialogsStore.ts | Updates the dialogs store to support onClose callbacks. |
app/stores/RootStore.ts | Registers the new ImportsStore. |
Comments suppressed due to low confidence (1)
plugins/notion/client/components/ImportDialog.tsx:89
- The dependency array for the handleStartImport callback includes 'name', which is not defined in this scope. Remove 'name' from the dependency array to avoid potential runtime errors.
}], [name, pagesWithPermission, onSubmit]);
app/hooks/usePolicy.ts
Outdated
export default function usePolicy(entity?: string | Model | null) { | ||
export default function usePolicy( | ||
entity?: string | Model | null, | ||
{ force }: { force: boolean } = { force: false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems dangerous, would rather not have it 😬
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm we need a way to reload policy when a property in entity
changes 🤔
In almost all cases, usePolicy
is used in a fresh page / render - so entity
remains fresh for the useEffect
dep reference checks. But for ImportListItem
, the ref remains the same as the realtime progress count keeps getting updated.
Perhaps, force-loading the import model in ImportListItem
is better as an one-off case?
plugins/notion/server/oauth.ts
Outdated
import { NotionUtils } from "../shared/NotionUtils"; | ||
import env from "./env"; | ||
|
||
export class NotionOAuth { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why this one deserves it's own class… could be static methods on NotionClient
perhaps
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea, can be included in NotionClient
.
server/constants.ts
Outdated
@@ -0,0 +1 @@ | |||
export const PagePerImportTask = 3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not worth a new file here
server/models/Import.ts
Outdated
@IsNumeric | ||
@Default(0) | ||
@Column(DataType.INTEGER) | ||
pageCount: number; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a more generic term here thinking this will be used for more than just Notion.
progress
?
completedCount
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking pageCount
is generic enough? In the end, a document or a page from an external source will be imported? 🤔
documentCount
maybe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
documentCount
is better - I would rather use our language than notion's
server/policies/import.ts
Outdated
import { allow } from "./cancan"; | ||
import { and, isTeamAdmin, isTeamMutable } from "./utils"; | ||
|
||
allow(User, ["createImport", "readImport"], Team, (actor, team) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
allow(User, ["createImport", "readImport"], Team, (actor, team) => | |
allow(User, ["createImport", "listImports"], Team, (actor, team) => |
This would be more typical
public async perform(event: ImportEvent) { | ||
const importModel = await Import.findByPk<Import<T>>(event.modelId, { | ||
rejectOnEmpty: true, | ||
paranoid: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably query with an update lock
server/routes/api/imports/imports.ts
Outdated
authorize(user, "readImport", user.team); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const where: WhereOptions<Import<any>> = service |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to include teamId
in query here
server/routes/api/imports/imports.ts
Outdated
const importModel = await Import.findByPk(id, { | ||
rejectOnEmpty: true, | ||
transaction, | ||
lock: transaction.LOCK.UPDATE, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for update lock or transaction here as we're only querying
server/routes/api/imports/imports.ts
Outdated
|
||
authorize(user, "createImport", user.team); | ||
|
||
const pendingImport = await Import.findOne({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nbd but could just use Import.count
here
|
||
const attachment = await Attachment.findByPk(item.attachmentId, { | ||
rejectOnEmpty: true, | ||
include: [{ association: "user" }], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not clear why user
is needed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my bad, not needed here.
server/queues/processors/ImportsProcessor.ts
10000
Outdated
const isRootDocument = | ||
!parentExternalId || !!importInput[parentExternalId]; | ||
|
||
await Document.createWithCtx( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On par with what's happening in existing file-based import flow, i.e. publish documents.create
event for each document.
I noticed that this leads to flood of requests from the app when the import is complete. I really think we should suppress this event, and retain just the collections.create
events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing you'll see is that a lot of processors suppress handling when the event is from an import, e.g.
event.data.source === "import" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, I was aware of it - current impl sets the data source.
But I didn't debug enough to find what was causing these additional requests, looks like WebsocketsProcessor
needs some updating (missing apiImportId
check) - will change it to check for event.data.source === "import"
outline/server/queues/processors/WebsocketsProcessor.ts
Lines 52 to 54 in 021a286
if (event.name === "documents.create" && document.importId) { | |
return; | |
} |
All comments are addressed, pushed a couple of improvements - lmk if something needs changing. Meanwhile, I'll work on adding a "cancel" option and some tests for APIs & tasks. |
Add policies to fileOperations.list Move policy reaction to model layer
This PR enables importing Notion documents using an API integration.
Pending tasks
Note: bigger PR, but not as big as it seems to be.. ~9k lines are related to tests.