Skip to content

Commit 9e90863

Browse files
emersionCastavoNicolasRitouetTguisnet
committed
✨(backend) add Notion import endpoint
Co-authored-by: Simon Ser <[email protected]> Co-authored-by: Baptiste Prevot <[email protected]> Co-authored-by: Nicolas Ritouet <[email protected]> Co-authored-by: Thibault Guisnet <[email protected]>
1 parent f4368ef commit 9e90863

File tree

5 files changed

+529
-0
lines changed

5 files changed

+529
-0
lines changed

src/backend/core/api/viewsets.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from django.db.models.expressions import RawSQL
1919
from django.db.models.functions import Left, Length
2020
from django.http import Http404, StreamingHttpResponse
21+
from django.shortcuts import redirect
2122
from django.urls import reverse
2223
from django.utils.functional import cached_property
2324
from django.utils.text import capfirst, slugify
@@ -37,6 +38,8 @@
3738
from core import authentication, choices, enums, models
3839
from core.services.ai_services import AIService
3940
from core.services.collaboration_services import CollaborationService
41+
from core.services.converter_services import YdocConverter
42+
from core.services.notion_import import import_notion
4043
from core.tasks.mail import send_ask_for_access_mail
4144
from core.utils import extract_attachments, filter_descendants
4245

@@ -2072,3 +2075,95 @@ def _load_theme_customization(self):
20722075
)
20732076

20742077
return theme_customization
2078+
2079+
2080+
@drf.decorators.api_view()
2081+
def notion_import_redirect(request):
2082+
query = urlencode(
2083+
{
2084+
"client_id": settings.NOTION_CLIENT_ID,
2085+
"response_type": "code",
2086+
"owner": "user",
2087+
"redirect_uri": settings.NOTION_REDIRECT_URI,
2088+
}
2089+
)
2090+
return redirect("https://api.notion.com/v1/oauth/authorize?" + query)
2091+
2092+
2093+
@drf.decorators.api_view()
2094+
def notion_import_callback(request):
2095+
code = request.GET.get("code")
2096+
resp = requests.post(
2097+
"https://api.notion.com/v1/oauth/token",
2098+
auth=requests.auth.HTTPBasicAuth(
2099+
settings.NOTION_CLIENT_ID, settings.NOTION_CLIENT_SECRET
2100+
),
2101+
headers={"Accept": "application/json"},
2102+
data={
2103+
"grant_type": "authorization_code",
2104+
"code": code,
2105+
"redirect_uri": settings.NOTION_REDIRECT_URI,
2106+
},
2107+
)
2108+
resp.raise_for_status()
2109+
data = resp.json()
2110+
request.session["notion_token"] = data["access_token"]
2111+
return redirect(f"{settings.FRONTEND_URL}/import-notion/")
2112+
2113+
2114+
def _import_notion_child_page(imported_doc, parent_doc, user, imported_docs_by_page_id):
2115+
document_content = YdocConverter().convert_blocks(imported_doc.blocks)
2116+
2117+
obj = parent_doc.add_child(
2118+
creator=user,
2119+
title=imported_doc.page.get_title() or "Child page",
2120+
content=document_content,
2121+
)
2122+
2123+
models.DocumentAccess.objects.create(
2124+
document=obj,
2125+
user=user,
2126+
role=models.RoleChoices.OWNER,
2127+
)
2128+
2129+
imported_docs_by_page_id[imported_doc.page.id] = obj
2130+
2131+
for child in imported_doc.children:
2132+
_import_notion_child_page(child, obj, user, imported_docs_by_page_id)
2133+
2134+
2135+
def _import_notion_root_page(imported_doc, user, imported_docs_by_page_id):
2136+
document_content = YdocConverter().convert_blocks(imported_doc.blocks)
2137+
2138+
obj = models.Document.add_root(
2139+
depth=1,
2140+
creator=user,
2141+
title=imported_doc.page.get_title() or "Imported Notion page",
2142+
link_reach=models.LinkReachChoices.RESTRICTED,
2143+
content=document_content,
2144+
)
2145+
2146+
models.DocumentAccess.objects.create(
2147+
document=obj,
2148+
user=user,
2149+
role=models.RoleChoices.OWNER,
2150+
)
2151+
2152+
imported_docs_by_page_id[imported_doc.page.id] = obj
2153+
2154+
for child in imported_doc.children:
2155+
_import_notion_child_page(child, obj, user, imported_docs_by_page_id)
2156+
2157+
2158+
@drf.decorators.api_view(["POST"])
2159+
def notion_import_run(request):
2160+
if "notion_token" not in request.session:
2161+
raise drf.exceptions.PermissionDenied()
2162+
2163+
imported_docs = import_notion(request.session["notion_token"])
2164+
2165+
imported_docs_by_page_id = {}
2166+
for imported_doc in imported_docs:
2167+
_import_notion_root_page(imported_doc, request.user, imported_docs_by_page_id)
2168+
2169+
return drf.response.Response({})

src/backend/core/notion_schemas/notion_block.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class NotionBlock(BaseModel):
1919
specific: "NotionBlockSpecifics"
2020
has_children: bool
2121
children: list["NotionBlock"] = Field(init=False, default_factory=list)
22+
# This is not part of the API response, but is used to store children blocks
2223

2324
@model_validator(mode="before")
2425
@classmethod

0 commit comments

Comments
 (0)