Skip to content

Commit 422352f

Browse files
committed
Adding data sources and updating databases for new Notion API version
1 parent f0bfbd8 commit 422352f

18 files changed

Lines changed: 587 additions & 278 deletions

python_notion_api/async_api/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
create_property_iterator,
77
)
88
from python_notion_api.async_api.notion_block import NotionBlock
9+
from python_notion_api.async_api.notion_data_source import NotionDataSource
910
from python_notion_api.async_api.notion_database import NotionDatabase
1011
from python_notion_api.async_api.notion_page import NotionPage
1112

@@ -14,6 +15,7 @@
1415
"NotionBlock",
1516
"NotionPage",
1617
"NotionDatabase",
18+
"NotionDataSource",
1719
"AsyncPropertyItemIterator",
1820
"AsyncRollupPropertyItemIterator",
1921
"AsyncBlockIterator",

python_notion_api/async_api/api.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from loguru import logger
99

1010
from python_notion_api.async_api.notion_block import NotionBlock
11+
from python_notion_api.async_api.notion_data_source import NotionDataSource
1112
from python_notion_api.async_api.notion_database import NotionDatabase
1213
from python_notion_api.async_api.notion_page import NotionPage
1314
from python_notion_api.async_api.retry_strategy import RetryStrategy
@@ -35,7 +36,7 @@ class AsyncNotionAPI:
3536
def __init__(
3637
self,
3738
access_token: str,
38-
api_version: str = "2022-06-28",
39+
api_version: str = "2025-09-03",
3940
page_limit: int = 20,
4041
rate_limit: tuple[int, int] = (500, 200),
4142
):
@@ -74,13 +75,25 @@ async def get_database(self, database_id: str) -> NotionDatabase:
7475

7576
return database
7677

77-
async def get_page(
78-
self, page_id: str, page_cast: type[NotionPage] = NotionPage
79-
) -> NotionPage:
80-
"""Gets Notion page.
78+
async def get_data_source(self, data_source_id: str) -> NotionDataSource:
79+
"""Gets Notion DataSource
8180
8281
Args:
83-
page_id: Id of the database to fetch.
82+
data_source_id: Id of the data source to fetch.
83+
84+
Returns:
85+
A Notion DataSource with the given id.
86+
"""
87+
data_source = NotionDataSource(self, data_source_id)
88+
await data_source.reload()
89+
90+
return data_source
91+
92+
async def get_page(self, page_id, page_cast=NotionPage) -> NotionPage:
93+
"""Gets Notion Page
94+
95+
Args:
96+
page_id: Id of the page to fetch.
8497
page_cast: A subclass of a NotionPage. Allows custom
8598
property retrieval.
8699
Returns:
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
from typing import Any, Dict, Generator, List, Optional, TYPE_CHECKING
2+
3+
from pydantic.v1 import BaseModel
4+
5+
from python_notion_api.async_api.notion_page import NotionPage
6+
from python_notion_api.async_api.utils import ensure_loaded
7+
from python_notion_api.models.common import FileObject, ParentObject
8+
from python_notion_api.models.configurations import (
9+
NotionPropertyConfiguration,
10+
RelationPropertyConfiguration,
11+
)
12+
from python_notion_api.models.filters import FilterItem
13+
from python_notion_api.models.objects import DataSource
14+
from python_notion_api.models.sorts import Sort
15+
from python_notion_api.models.values import PropertyValue, generate_value
16+
17+
if TYPE_CHECKING:
18+
from python_notion_api.async_api.api import AsyncNotionAPI
19+
20+
21+
class NotionDataSource:
22+
"""Wrapper for a Notion datasource object.
23+
24+
Args:
25+
api: Instance of the NotionAPI.
26+
data_source_id: Id of the data source.
27+
"""
28+
29+
class CreatePageRequest(BaseModel):
30+
parent: ParentObject
31+
properties: Dict[str, PropertyValue]
32+
cover: Optional[FileObject]
33+
34+
def __init__(self, api: "AsyncNotionAPI", data_source_id: str):
35+
self._api = api
36+
self._data_source_id = data_source_id
37+
self._object = None
38+
self._properties = None
39+
self._title = None
40+
41+
@ensure_loaded
42+
def __getattr__(self, attr_key):
43+
return getattr(self._object, attr_key)
44+
45+
@property
46+
def data_source_id(self) -> str:
47+
return self._data_source_id.replace("-", "")
48+
49+
async def reload(self):
50+
self._object = await self._api._get(
51+
endpoint=f"data_sources/{self._data_source_id}",
52+
cast_cls=DataSource,
53+
)
54+
55+
if self._object is None:
56+
raise Exception(
57+
f"Error loading data source {self._data_source_id}"
58+
)
59+
60+
self._properties = {
61+
key: NotionPropertyConfiguration.from_obj(val)
62+
for key, val in self._object.properties.items()
63+
}
64+
self._title = "".join(rt.plain_text for rt in self._object.title)
65+
66+
async def query(
67+
self,
68+
filters: Optional[FilterItem] = None,
69+
sorts: Optional[List[Sort]] = None,
70+
page_limit: Optional[int] = None,
71+
cast_cls=NotionPage,
72+
) -> Generator[NotionPage, None, None]:
73+
"""A wrapper for 'Query a data source' action.
74+
75+
Retrieves all pages belonging to the data source.
76+
77+
Args:
78+
filters:
79+
sorts:
80+
cast_cls: A subclass of a NotionPage. Allows custom
81+
property retrieval
82+
83+
"""
84+
data = {}
85+
if filters is not None:
86+
filters = filters.dict(by_alias=True, exclude_unset=True)
87+
data["filter"] = filters
88+
89+
if sorts is not None:
90+
data["sorts"] = [
91+
sort.dict(by_alias=True, exclude_unset=True) for sort in sorts
92+
]
93+
94+
async for item in self._api._post_iterate(
95+
endpoint=f"data_sources/{self._data_source_id}/query",
96+
data=data,
97+
page_limit=page_limit,
98+
):
99+
yield cast_cls(
100+
api=self._api, data_source=self, page_id=item.page_id, obj=item
101+
)
102+
103+
@property
104+
@ensure_loaded
105+
def title(self) -> str:
106+
"""Get the title of the data source."""
107+
return self._title
108+
109+
@property
110+
@ensure_loaded
111+
def properties(self) -> Dict[str, NotionPropertyConfiguration]:
112+
"""Get all property configurations of the data source."""
113+
return self._properties
114+
115+
@property
116+
@ensure_loaded
117+
def relations(self) -> Dict[str, RelationPropertyConfiguration]:
118+
"""Get all property configurations of the data source that are
119+
relations.
120+
"""
121+
return {
122+
key: val
123+
for key, val in self._properties.items()
124+
if isinstance(val, RelationPropertyConfiguration)
125+
}
126+
127+
async def create_page(
128+
self,
129+
properties: Dict[str, Any] = {},
130+
cover_url: Optional[str] = None,
131+
) -> NotionPage:
132+
"""Creates a new page in the Data Source and updates the new page with
133+
the properties.
134+
135+
Args:
136+
properties: Dictionary of property names and values. Value types
137+
will depend on the property type. Can be the raw value
138+
(e.g. string, float) or an object (e.g. SelectValue,
139+
NumberPropertyItem)
140+
cover_url: URL of an image for the page cover.
141+
"""
142+
143+
validated_properties = {}
144+
for prop_name, prop_value in properties.items():
145+
prop = self.properties.get(prop_name, None)
146+
if prop is None:
147+
raise ValueError(f"Unknown property: {prop_name}")
148+
value = generate_value(prop.config_type, prop_value)
149+
validated_properties[prop_name] = value
150+
151+
request = NotionDataSource.CreatePageRequest(
152+
parent=ParentObject(
153+
type="data_source_id", data_source_id=self.data_source_id
154+
),
155+
properties=validated_properties,
156+
cover=(
157+
FileObject.from_url(cover_url)
158+
if cover_url is not None
159+
else None
160+
),
161+
)
162+
163+
data = request.json(by_alias=True, exclude_unset=True)
164+
165+
new_page = await self._api._post("pages", data=data)
166+
167+
return NotionPage(
168+
api=self._api,
169+
page_id=new_page.page_id,
170+
obj=new_page,
171+
data_source=self,
172+
)

0 commit comments

Comments
 (0)