notion-client 0.1.0.1 → 0.2.0.0
raw patch · 8 files changed
+81/−13 lines, 8 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Notion.V1: [retrievePageMarkdown] :: Methods -> PageID -> Maybe Bool -> IO PageMarkdown
+ Notion.V1.Pages: PageMarkdown :: PageID -> Text -> Bool -> Vector UUID -> PageMarkdown
+ Notion.V1.Pages: [markdown] :: PageMarkdown -> Text
+ Notion.V1.Pages: [truncated] :: PageMarkdown -> Bool
+ Notion.V1.Pages: [unknownBlockIds] :: PageMarkdown -> Vector UUID
+ Notion.V1.Pages: data PageMarkdown
+ Notion.V1.Pages: instance Data.Aeson.Types.FromJSON.FromJSON Notion.V1.Pages.PageMarkdown
+ Notion.V1.Pages: instance Data.Aeson.Types.ToJSON.ToJSON Notion.V1.Pages.PageMarkdown
+ Notion.V1.Pages: instance GHC.Internal.Generics.Generic Notion.V1.Pages.PageMarkdown
+ Notion.V1.Pages: instance GHC.Internal.Show.Show Notion.V1.Pages.PageMarkdown
- Notion.V1: Methods :: (CreateDatabase -> IO DatabaseObject) -> (DatabaseID -> IO DatabaseObject) -> (DatabaseID -> UpdateDatabase -> IO DatabaseObject) -> (DatabaseID -> QueryDatabase -> IO (ListOf PageObject)) -> (DataSourceID -> IO DataSourceObject) -> (CreateDataSource -> IO DataSourceObject) -> (DataSourceID -> UpdateDataSource -> IO DataSourceObject) -> (DataSourceID -> QueryDataSource -> IO (ListOf PageObject)) -> (CreatePage -> IO PageObject) -> (PageID -> IO PageObject) -> (PageID -> UpdatePage -> IO PageObject) -> (BlockID -> IO BlockObject) -> (BlockID -> BlockContent -> IO BlockObject) -> (ParentID -> Maybe Natural -> Maybe Text -> IO (ListOf BlockObject)) -> (ParentID -> AppendBlockChildren -> IO (ListOf BlockObject)) -> (BlockID -> IO BlockObject) -> (UserID -> IO UserObject) -> (Maybe Natural -> Maybe Text -> IO (ListOf UserObject)) -> IO UserObject -> (SearchRequest -> IO (ListOf Value)) -> (CreateComment -> IO CommentObject) -> (Maybe BlockID -> Maybe Text -> Maybe Natural -> IO (ListOf CommentObject)) -> Methods
+ Notion.V1: Methods :: (CreateDatabase -> IO DatabaseObject) -> (DatabaseID -> IO DatabaseObject) -> (DatabaseID -> UpdateDatabase -> IO DatabaseObject) -> (DatabaseID -> QueryDatabase -> IO (ListOf PageObject)) -> (DataSourceID -> IO DataSourceObject) -> (CreateDataSource -> IO DataSourceObject) -> (DataSourceID -> UpdateDataSource -> IO DataSourceObject) -> (DataSourceID -> QueryDataSource -> IO (ListOf PageObject)) -> (CreatePage -> IO PageObject) -> (PageID -> IO PageObject) -> (PageID -> UpdatePage -> IO PageObject) -> (PageID -> Maybe Bool -> IO PageMarkdown) -> (BlockID -> IO BlockObject) -> (BlockID -> BlockContent -> IO BlockObject) -> (ParentID -> Maybe Natural -> Maybe Text -> IO (ListOf BlockObject)) -> (ParentID -> AppendBlockChildren -> IO (ListOf BlockObject)) -> (BlockID -> IO BlockObject) -> (UserID -> IO UserObject) -> (Maybe Natural -> Maybe Text -> IO (ListOf UserObject)) -> IO UserObject -> (SearchRequest -> IO (ListOf Value)) -> (CreateComment -> IO CommentObject) -> (Maybe BlockID -> Maybe Text -> Maybe Natural -> IO (ListOf CommentObject)) -> Methods
- Notion.V1.DataSources: DataSourceObject :: DataSourceID -> POSIXTime -> POSIXTime -> UserReference -> UserReference -> Vector RichText -> Vector RichText -> Value -> Text -> Parent -> Maybe Parent -> Bool -> Maybe Bool -> Maybe Bool -> Maybe Text -> Maybe Icon -> Maybe Cover -> ObjectType -> DataSourceObject
+ Notion.V1.DataSources: DataSourceObject :: DataSourceID -> POSIXTime -> POSIXTime -> UserReference -> UserReference -> Vector RichText -> Vector RichText -> Value -> Text -> Parent -> Maybe Parent -> Maybe Bool -> Maybe Bool -> Maybe Bool -> Maybe Text -> Maybe Icon -> Maybe Cover -> ObjectType -> DataSourceObject
- Notion.V1.Pages: [id] :: SelectOption -> Maybe Text
+ Notion.V1.Pages: [id] :: PageMarkdown -> PageID
- Notion.V1.Pages: type API = "pages" :> Capture "page_id" PageID :> Get '[JSON] PageObject :<|> ReqBody '[JSON] CreatePage :> Post '[JSON] PageObject :<|> Capture "page_id" PageID :> ReqBody '[JSON] UpdatePage :> Patch '[JSON] PageObject
+ Notion.V1.Pages: type API = "pages" :> Capture "page_id" PageID :> Get '[JSON] PageObject :<|> ReqBody '[JSON] CreatePage :> Post '[JSON] PageObject :<|> Capture "page_id" PageID :> ReqBody '[JSON] UpdatePage :> Patch '[JSON] PageObject :<|> Capture "page_id" PageID :> "markdown" :> QueryParam "include_transcript" Bool :> Get '[JSON] PageMarkdown
Files
- CHANGELOG.md +13/−0
- notion-client.cabal +1/−1
- src/Notion/V1.hs +8/−2
- src/Notion/V1/Blocks.hs +4/−2
- src/Notion/V1/DataSources.hs +3/−2
- src/Notion/V1/Databases.hs +2/−1
- src/Notion/V1/Pages.hs +27/−1
- tasty/Main.hs +23/−4
CHANGELOG.md view
@@ -1,5 +1,18 @@ # Changelog for notion-client +## 0.2.0.0 (2026-03-29)++### Breaking Changes+* Bump Notion API version from `2025-09-03` to `2026-03-11`+* Change `DataSourceObject.archived` from `Bool` to `Maybe Bool` (field no longer guaranteed in API responses)++### New Features+* Add `PageMarkdown` type and `retrievePageMarkdown` method for `GET /v1/pages/{page_id}/markdown`+* Support optional `include_transcript` query parameter for markdown retrieval++### Bug Fixes+* Handle API rename of `archived` to `is_archived` in responses for `PageObject`, `BlockObject`, `DatabaseObject`, and `DataSourceObject`+ ## 0.1.0.1 (2026-03-29) ### Bug Fixes
notion-client.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.4 name: notion-client-version: 0.1.0.1+version: 0.2.0.0 synopsis: Type-safe Haskell client for the Notion API description: This package provides comprehensive and type-safe bindings
src/Notion/V1.hs view
@@ -48,7 +48,7 @@ import Notion.V1.Databases (CreateDatabase, DatabaseID, DatabaseObject, QueryDatabase, UpdateDatabase) import Notion.V1.Databases qualified as Databases import Notion.V1.ListOf (ListOf (..))-import Notion.V1.Pages (CreatePage, PageID, PageObject, UpdatePage)+import Notion.V1.Pages (CreatePage, PageID, PageMarkdown, PageObject, UpdatePage) import Notion.V1.Pages qualified as Pages import Notion.V1.Search (SearchRequest) import Notion.V1.Search qualified as Search@@ -76,7 +76,7 @@ Methods makeMethods clientEnv token = Methods {..} where- notionVersion = "2025-09-03" -- Notion API version with data source support+ notionVersion = "2026-03-11" -- Notion API version with markdown content support -- If you experience 400 errors, check for updated versions at -- https://developers.notion.com/reference/versioning ( ( createDatabase@@ -92,6 +92,7 @@ :<|> ( retrievePage :<|> createPage :<|> updatePage+ :<|> retrievePageMarkdown ) :<|> ( retrieveBlock :<|> updateBlock@@ -140,6 +141,11 @@ createPage :: CreatePage -> IO PageObject, retrievePage :: PageID -> IO PageObject, updatePage :: PageID -> UpdatePage -> IO PageObject,+ retrievePageMarkdown ::+ PageID ->+ Maybe Bool ->+ -- \^ include_transcript+ IO PageMarkdown, -- \* Blocks retrieveBlock :: BlockID -> IO BlockObject, updateBlock :: BlockID -> Blocks.BlockContent -> IO BlockObject,
src/Notion/V1/Blocks.hs view
@@ -11,9 +11,11 @@ ) where -import Data.Aeson ((.:), (.=))+import Control.Applicative ((<|>))+import Data.Aeson ((.:), (.:?), (.=)) import Data.Aeson qualified as Aeson import Data.Aeson.Key qualified as Key+import Data.Maybe (fromMaybe) import Notion.Prelude import Notion.V1.Common (BlockID, ObjectType (..), Parent) import Notion.V1.ListOf (ListOf)@@ -48,7 +50,7 @@ createdBy <- o .: "created_by" lastEditedBy <- o .: "last_edited_by" hasChildren <- o .: "has_children"- archived <- o .: "archived"+ archived <- fmap (fromMaybe False) (o .:? "is_archived" <|> o .:? "archived") type_ <- o .: "type" -- Content is stored under a field named after the block type (e.g., "heading_1", "paragraph") content <- o .: Key.fromText type_
src/Notion/V1/DataSources.hs view
@@ -15,6 +15,7 @@ ) where +import Control.Applicative ((<|>)) import Data.Aeson ((.:), (.:?)) import Notion.Prelude import Notion.V1.Common (Cover, Icon, ObjectType, Parent, UUID)@@ -40,7 +41,7 @@ url :: Text, parent :: Parent, databaseParent :: Maybe Parent,- archived :: Bool,+ archived :: Maybe Bool, isInline :: Maybe Bool, inTrash :: Maybe Bool, publicUrl :: Maybe Text,@@ -66,7 +67,7 @@ url <- o .: "url" parent <- o .: "parent" databaseParent <- o .:? "database_parent"- archived <- o .: "archived"+ archived <- o .:? "is_archived" <|> o .:? "archived" isInline <- o .:? "is_inline" inTrash <- o .:? "in_trash" publicUrl <- o .:? "public_url"
src/Notion/V1/Databases.hs view
@@ -14,6 +14,7 @@ ) where +import Control.Applicative ((<|>)) import Data.Aeson ((.:), (.:?)) import Notion.Prelude import Notion.V1.Common (Cover, Icon, ObjectType (..), Parent, UUID)@@ -81,7 +82,7 @@ cover <- o .:? "cover" url <- o .: "url" parent <- o .: "parent"- archived <- o .:? "archived"+ archived <- o .:? "is_archived" <|> o .:? "archived" isInline <- o .:? "is_inline" inTrash <- o .:? "in_trash" isLocked <- o .:? "is_locked"
src/Notion/V1/Pages.hs view
@@ -13,15 +13,20 @@ mkCreatePage, mkUpdatePage, + -- * Markdown+ PageMarkdown (..),+ -- * Servant API, ) where +import Control.Applicative ((<|>)) import Data.Aeson ((.:), (.:?), (.=)) import Data.Aeson qualified as Aeson import Data.Aeson.Key qualified as Key import Data.Aeson.KeyMap qualified as KeyMap+import Data.Maybe (fromMaybe) import Notion.Prelude hiding (Number) import Notion.V1.Common (Cover, Icon, ObjectType (..), Parent, UUID) import Notion.V1.Users (UserReference)@@ -60,7 +65,7 @@ cover <- o .:? "cover" icon <- o .:? "icon" parent <- o .: "parent"- archived <- o .: "archived"+ archived <- fmap (fromMaybe False) (o .:? "is_archived" <|> o .:? "archived") inTrash <- o .: "in_trash" properties <- o .: "properties" url <- o .: "url"@@ -267,6 +272,23 @@ instance ToJSON SelectOption where toJSON = genericToJSON aesonOptions +-- | Response from @GET \/v1\/pages\/{page_id}\/markdown@+--+-- Contains the page content rendered as Notion-flavored enhanced markdown.+data PageMarkdown = PageMarkdown+ { id :: PageID,+ markdown :: Text,+ truncated :: Bool,+ unknownBlockIds :: Vector UUID+ }+ deriving stock (Generic, Show)++instance FromJSON PageMarkdown where+ parseJSON = genericParseJSON aesonOptions++instance ToJSON PageMarkdown where+ toJSON = genericToJSON aesonOptions+ -- | Servant API type API = "pages"@@ -277,4 +299,8 @@ :<|> Capture "page_id" PageID :> ReqBody '[JSON] UpdatePage :> Patch '[JSON] PageObject+ :<|> Capture "page_id" PageID+ :> "markdown"+ :> QueryParam "include_transcript" Bool+ :> Get '[JSON] PageMarkdown )
tasty/Main.hs view
@@ -2,7 +2,9 @@ import Data.Text qualified as Text import Notion.V1+import Notion.V1.Common (UUID (..)) import Notion.V1.ListOf (ListOf (..))+import Notion.V1.Pages (PageMarkdown (..)) import Notion.V1.Search (SearchRequest (..)) import Notion.V1.Users (UserObject (..)) import System.Environment qualified as Environment@@ -31,13 +33,24 @@ clientEnv <- getClientEnv "https://api.notion.com/v1" let methods = makeMethods clientEnv (Text.pack token) + mPageId <- lookupEnv "NOTION_TEST_PAGE_ID"++ let markdownTests = case mPageId of+ Just pageId ->+ [ testCase "Retrieve page as markdown" $+ testRetrievePageMarkdown methods (Text.pack pageId)+ ]+ Nothing -> []+ pure $ testGroup "Notion API Tests"- [ testCase "Retrieve current user" $ testRetrieveCurrentUser methods,- testCase "List users" $ testListUsers methods,- testCase "Timestamp parsing works" $ testSearchAPI methods- ]+ ( [ testCase "Retrieve current user" $ testRetrieveCurrentUser methods,+ testCase "List users" $ testListUsers methods,+ testCase "Timestamp parsing works" $ testSearchAPI methods+ ]+ <> markdownTests+ ) testRetrieveCurrentUser :: Methods -> Assertion testRetrieveCurrentUser Methods {retrieveMyUser} = do@@ -65,6 +78,12 @@ _ <- search searchParams -- If this test runs without errors, it means timestamp parsing works assertBool "Search should run without timestamp parsing errors" True++testRetrievePageMarkdown :: Methods -> Text.Text -> Assertion+testRetrievePageMarkdown Methods {retrievePageMarkdown} pageId = do+ result <- retrievePageMarkdown (UUID pageId) Nothing+ let PageMarkdown {markdown = md} = result+ assertBool "Markdown content should not be empty" (not $ Text.null md) lookupEnv :: String -> IO (Maybe String) lookupEnv var = Environment.lookupEnv var