notion-client-effectful (empty) → 0.2.0.0
raw patch · 7 files changed
+1069/−0 lines, 7 filesdep +aesondep +basedep +effectful-core
Dependencies added: aeson, base, effectful-core, notion-client, text
Files
- CHANGELOG.md +21/−0
- LICENSE +21/−0
- README.md +115/−0
- notion-client-effectful.cabal +62/−0
- src/Notion/V1/Effectful.hs +137/−0
- src/Notion/V1/Effectful/Effect.hs +534/−0
- src/Notion/V1/Effectful/Interpreter.hs +179/−0
+ CHANGELOG.md view
@@ -0,0 +1,21 @@+# Changelog for `notion-client-effectful`++## 0.2.0.0 - 2026-09-15++### Breaking Changes++* Requires `notion-client >=0.8 && <0.9`.+* `queryDataSource` and `search` now return `ListOf PageOrDataSource` (was `ListOf PageObject` and `ListOf Value`), following `notion-client`.+* `updateBlock` takes `Blocks.BlockUpdatePayload` (was `Blocks.BlockUpdate`).+* `createComment` returns `CommentResponse` (was `CommentObject`).+* Remove `queryView` / `QueryView`; use `createViewQuery`, `getViewQueryResults` and `deleteViewQuery`.++### New Features++* New operations `createPageFiltered`, `updatePageFiltered`, `createPageAsync`, `updatePageMarkdownAsync`, `retrieveAsyncTask`, `retrieveComment`, `updateComment`, `deleteComment`, `createViewQuery`, `getViewQueryResults`, `deleteViewQuery`, `createMeetingNote` and `queryMeetingNotes`.++## 0.1.0.0 - 2026-04-17++* Initial release: `Notion` effect and `runNotion` interpreter covering+ every `Notion.V1.Methods` field at the time of release+ (notion-client 0.7.x).
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2024 Nadeem Bitar++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,115 @@+# notion-client-effectful++An [effectful][effectful] surface for+[`notion-client`](https://hackage.haskell.org/package/notion-client).++[effectful]: https://hackage.haskell.org/package/effectful++This package wraps the `Notion.V1.Methods` record as a dynamic+effect — so code written against `effectful` does not have to+thread the `Methods` value through every call site and does not+have to drop into `IO` at each API boundary.++## Purpose++`notion-client` exposes a single `Methods` record whose fields+are `IO`-typed API calls. That is ergonomic for a plain-`IO`+program but awkward for an `effectful`-based caller: every call+requires `liftIO` and escapes the ambient `Error` / `Reader` /+`Log` stack.++`notion-client-effectful` gives that caller an equivalent set of+operations surfaced as an `Effect` so the stack is preserved.++## Import pattern++Every smart constructor shares its name with the matching+`Notion.V1.Methods` record selector. That is on purpose — it+keeps migration from IO mechanical — but it means a file that+imports both modules unqualified will see name clashes. Import+one of the two qualified:++```haskell+import Notion.V1 (Methods, getClientEnv, makeMethods)+import Notion.V1.Effectful qualified as NE+```++Then `NE.retrievePage`, `NE.search`, `NE.runNotion`, and so on.++## Error handling++`runNotion` catches `Notion.V1.Error.NotionError` thrown by the+underlying `Methods` and re-raises it via the `Error NotionError`+effect, so callers can branch on API error shapes+(`object_not_found`, validation failures, etc.) without reaching+for `IO`-level exception handling.++Other `Servant.Client.ClientError` values — network failures,+decoding errors — are *not* caught and remain `IO` exceptions.+This preserves the contract of the underlying `notion-client`+library and lets callers layer their own `Error ClientError`+interpretation on top later. The same applies to+`UnknownHTTPResponseError`, `RequestTimeoutError` and+`InvalidPathParameterError` from `Notion.V1.Error`. Retries of+rate-limited requests happen inside `Methods`, before an error reaches+`runNotion`.++## Minimum viable usage++```haskell+module Demo where++import Data.Text (Text)+import Data.Text qualified as Text+import Effectful (Eff, runEff, (:>))+import Effectful.Error.Static (Error, runErrorNoCallStack)+import Notion.V1 (getClientEnv, makeMethods)+import Notion.V1.Common (UUID (..))+import Notion.V1.Effectful qualified as NE+import Notion.V1.Error (NotionError)+import System.Environment qualified as Env++demo ::+ (NE.Notion :> es, Error NotionError :> es) =>+ UUID ->+ Eff es Text+demo pid = do+ page <- NE.retrievePage pid+ pure (Text.pack (show page))++main :: IO ()+main = do+ token <- Text.pack <$> Env.getEnv "NOTION_TOKEN"+ env <- getClientEnv "https://api.notion.com/v1"+ let methods = makeMethods env token+ result <-+ runEff+ . runErrorNoCallStack @NotionError+ . NE.runNotion methods+ $ demo (UUID "00000000-0000-0000-0000-000000000000")+ print result+```++## What you get++One smart constructor per field of `Notion.V1.Methods`. The+argument types and order match the underlying field, so an+existing call like++```haskell+page <- retrievePage methods pid+```++becomes++```haskell+page <- NE.retrievePage pid+```++with no other change to the call site.++## Versioning++The `0.y.z` series tracks `notion-client` 0.7.x. A major version+bump is planned if `notion-client` changes the shape of `Methods`+in a way that forces a constructor rename.
+ notion-client-effectful.cabal view
@@ -0,0 +1,62 @@+cabal-version: 3.4+name: notion-client-effectful+version: 0.2.0.0+synopsis: Effectful effects for notion-client+description:+ Effectful effect + default interpreter for shinzui/notion-client.+ Exposes every operation on 'Notion.V1.Methods' as a smart+ constructor of the 'Notion.V1.Effectful.Notion' effect and provides+ a 'runNotion' interpreter that dispatches through a concrete+ @Methods@ value. API-level 'Notion.V1.Error.NotionError' responses+ are surfaced via the @Error NotionError@ effect; other+ @Servant.Client.ClientError@ values remain 'IO' exceptions.+ See @README.md@ for the full import pattern and a worked example.++license: MIT+license-file: LICENSE+author: Nadeem Bitar+maintainer: nadeem@gmail.com+category: Web+build-type: Simple+extra-doc-files:+ CHANGELOG.md+ README.md++common warnings+ ghc-options:+ -Wall+ -Wcompat+ -Widentities+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ -Wredundant-constraints+ -fhide-source-paths+ -Wmissing-export-lists+ -Wpartial-fields+ -Wmissing-deriving-strategies++library+ import: warnings+ exposed-modules:+ Notion.V1.Effectful+ Notion.V1.Effectful.Effect+ Notion.V1.Effectful.Interpreter++ default-extensions:+ DataKinds+ DuplicateRecordFields+ GADTs+ LambdaCase+ OverloadedStrings+ TypeFamilies+ TypeOperators++ build-depends:+ aeson >=2.2 && <2.3,+ base >=4.18 && <5,+ effectful-core >=2.5 && <3,+ notion-client >=0.8 && <0.9,+ text >=2.0 && <2.2,++ hs-source-dirs: src+ default-language: GHC2024
+ src/Notion/V1/Effectful.hs view
@@ -0,0 +1,137 @@+-- | Effectful surface for @notion-client@.+--+-- This module re-exports the 'Notion' effect, its smart constructors,+-- and the default 'runNotion' interpreter. A caller typically writes:+--+-- @+-- import Notion.V1 (Methods, getClientEnv, makeMethods)+-- import Notion.V1.Effectful qualified as NE+-- @+--+-- and then uses @NE.retrievePage@, @NE.search@, @NE.runNotion@, etc.+-- The qualified import avoids name clashes with the matching+-- 'Notion.V1.Methods' record selectors — every smart constructor+-- here shares its name with a field of @Methods@ on purpose, so+-- migrating an IO call site is a near-mechanical rewrite.+--+-- Error handling: 'runNotion' catches 'NotionError' from the+-- underlying 'Methods' value and re-throws via the @Error NotionError@+-- effect. Other 'Servant.Client.ClientError' values (network+-- failures, decoding errors) remain 'IO' exceptions.+module Notion.V1.Effectful+ ( -- * Effect+ Notion,++ -- * Interpreter+ runNotion,++ -- * Databases+ createDatabase,+ retrieveDatabase,+ updateDatabase,+ queryDatabase,++ -- * Data Sources+ retrieveDataSource,+ createDataSource,+ updateDataSource,+ queryDataSource,+ listDataSourceTemplates,++ -- * Pages+ createPage,+ retrievePage,+ retrievePageFiltered,+ updatePage,+ retrievePageProperty,+ retrievePageMarkdown,+ updatePageMarkdown,+ movePage,++ -- * Blocks+ retrieveBlock,+ updateBlock,+ listBlockChildren,+ appendBlockChildren,+ deleteBlock,++ -- * Users+ retrieveUser,+ listUsers,+ retrieveMyUser,++ -- * Search+ search,++ -- * Comments+ createComment,+ listComments,++ -- * Views+ createView,+ retrieveView,+ updateView,+ deleteView,+ listViews,+ createViewQuery,+ getViewQueryResults,+ deleteViewQuery,++ -- * Custom Emojis+ listCustomEmojis,++ -- * File Uploads+ createFileUpload,+ retrieveFileUpload,+ sendFileUploadContent,+ completeFileUpload,+ listFileUploads,+ )+where++import Notion.V1.Effectful.Effect+ ( Notion,+ appendBlockChildren,+ completeFileUpload,+ createComment,+ createDataSource,+ createDatabase,+ createFileUpload,+ createPage,+ createView,+ createViewQuery,+ deleteBlock,+ deleteView,+ deleteViewQuery,+ getViewQueryResults,+ listBlockChildren,+ listComments,+ listCustomEmojis,+ listDataSourceTemplates,+ listFileUploads,+ listUsers,+ listViews,+ movePage,+ queryDataSource,+ queryDatabase,+ retrieveBlock,+ retrieveDataSource,+ retrieveDatabase,+ retrieveFileUpload,+ retrieveMyUser,+ retrievePage,+ retrievePageFiltered,+ retrievePageMarkdown,+ retrievePageProperty,+ retrieveUser,+ retrieveView,+ search,+ sendFileUploadContent,+ updateBlock,+ updateDataSource,+ updateDatabase,+ updatePage,+ updatePageMarkdown,+ updateView,+ )+import Notion.V1.Effectful.Interpreter (runNotion)
+ src/Notion/V1/Effectful/Effect.hs view
@@ -0,0 +1,534 @@+-- | The 'Notion' effect and its smart constructors.+--+-- One constructor per field of 'Notion.V1.Methods'. Each smart+-- constructor has the same name, same argument order, and same+-- argument types as the corresponding @Methods@ field — so migrating+-- an 'IO'-based call site amounts to dropping the explicit+-- @methods@ argument.+--+-- /Import note./ Every smart constructor name clashes with the+-- matching 'Notion.V1.Methods' record selector. Import one of the+-- two modules qualified (mirroring how 'Notion.V1' itself qualifies+-- @Pages@, @Databases@, etc.).+module Notion.V1.Effectful.Effect+ ( -- * Effect+ Notion (..),++ -- * Databases+ createDatabase,+ retrieveDatabase,+ updateDatabase,+ queryDatabase,++ -- * Data Sources+ retrieveDataSource,+ createDataSource,+ updateDataSource,+ queryDataSource,+ listDataSourceTemplates,++ -- * Pages+ createPage,+ createPageFiltered,+ retrievePage,+ retrievePageFiltered,+ updatePage,+ updatePageFiltered,+ retrievePageProperty,+ retrievePageMarkdown,+ updatePageMarkdown,+ movePage,+ createPageAsync,+ updatePageMarkdownAsync,++ -- * Blocks+ retrieveBlock,+ updateBlock,+ listBlockChildren,+ appendBlockChildren,+ deleteBlock,++ -- * Users+ retrieveUser,+ listUsers,+ retrieveMyUser,++ -- * Search+ search,++ -- * Comments+ createComment,+ listComments,+ retrieveComment,+ updateComment,+ deleteComment,++ -- * Views+ createView,+ retrieveView,+ updateView,+ deleteView,+ listViews,+ createViewQuery,+ getViewQueryResults,+ deleteViewQuery,++ -- * Custom Emojis+ listCustomEmojis,++ -- * File Uploads+ createFileUpload,+ retrieveFileUpload,+ sendFileUploadContent,+ completeFileUpload,+ listFileUploads,++ -- * Async Tasks+ retrieveAsyncTask,++ -- * Meeting Notes+ createMeetingNote,+ queryMeetingNotes,+ )+where++import Data.Text (Text)+import Effectful (Dispatch (..), DispatchOf, Eff, Effect, (:>))+import Effectful.Dispatch.Dynamic (send)+import Notion.V1.AsyncTasks (AsyncOr, AsyncTask, AsyncTaskID)+import Notion.V1.Blocks (BlockID, BlockObject)+import Notion.V1.Blocks qualified as Blocks+import Notion.V1.Comments (CommentObject, CommentResponse)+import Notion.V1.Comments qualified as Comments+import Notion.V1.Common (ParentID, UUID)+import Notion.V1.CustomEmojis (CustomEmoji)+import Notion.V1.DataSources (DataSourceID, DataSourceObject)+import Notion.V1.DataSources qualified as DataSources+import Notion.V1.Databases (CreateDatabase, DatabaseID, DatabaseObject, QueryDatabase, UpdateDatabase)+import Notion.V1.FileUploads (FileUploadID, FileUploadObject, FileUploadStatus)+import Notion.V1.FileUploads qualified as FileUploads+import Notion.V1.ListOf (ListOf)+import Notion.V1.MeetingNotes qualified as MeetingNotes+import Notion.V1.Pages (CreatePage, MovePage, PageID, PageMarkdown, PageObject, PartialPageObject, PropertyItemResponse, UpdatePage, UpdatePageMarkdown)+import Notion.V1.Search (PageOrDataSource, SearchRequest)+import Notion.V1.Users (UserID, UserObject)+import Notion.V1.Views (ViewObject)+import Notion.V1.Views qualified as Views+import Numeric.Natural (Natural)++-- | Operations on a Notion workspace.+--+-- One constructor per field of 'Notion.V1.Methods'. The constructor+-- names are the @PascalCase@ form of the field names; argument order+-- matches the field signature.+data Notion :: Effect where+ -- Databases+ CreateDatabase :: CreateDatabase -> Notion m DatabaseObject+ RetrieveDatabase :: DatabaseID -> Notion m DatabaseObject+ UpdateDatabase :: DatabaseID -> UpdateDatabase -> Notion m DatabaseObject+ QueryDatabase :: DatabaseID -> QueryDatabase -> Notion m (ListOf PageObject)+ -- Data Sources+ RetrieveDataSource :: DataSourceID -> Notion m DataSourceObject+ CreateDataSource :: DataSources.CreateDataSource -> Notion m DataSourceObject+ UpdateDataSource :: DataSourceID -> DataSources.UpdateDataSource -> Notion m DataSourceObject+ QueryDataSource :: DataSourceID -> DataSources.QueryDataSource -> Notion m (ListOf DataSources.PageOrDataSource)+ ListDataSourceTemplates ::+ DataSourceID ->+ Maybe Text ->+ Maybe Text ->+ Maybe Natural ->+ Notion m DataSources.ListTemplatesResponse+ -- Pages+ CreatePage :: CreatePage -> Notion m PageObject+ CreatePageFiltered :: [Text] -> CreatePage -> Notion m PageObject+ RetrievePage :: PageID -> Notion m PageObject+ RetrievePageFiltered :: PageID -> [Text] -> Notion m PageObject+ UpdatePage :: PageID -> UpdatePage -> Notion m PageObject+ UpdatePageFiltered :: PageID -> [Text] -> UpdatePage -> Notion m PageObject+ RetrievePageProperty ::+ PageID ->+ Text ->+ Maybe Text ->+ Maybe Natural ->+ Notion m PropertyItemResponse+ RetrievePageMarkdown :: PageID -> Maybe Bool -> Notion m PageMarkdown+ UpdatePageMarkdown :: PageID -> UpdatePageMarkdown -> Notion m PageMarkdown+ MovePage :: PageID -> MovePage -> Notion m PageObject+ CreatePageAsync :: CreatePage -> Notion m (AsyncOr PageObject)+ UpdatePageMarkdownAsync :: PageID -> UpdatePageMarkdown -> Notion m (AsyncOr PageMarkdown)+ -- Blocks+ RetrieveBlock :: BlockID -> Notion m BlockObject+ UpdateBlock :: BlockID -> Blocks.BlockUpdatePayload -> Notion m BlockObject+ ListBlockChildren :: ParentID -> Maybe Natural -> Maybe Text -> Notion m (ListOf BlockObject)+ AppendBlockChildren :: ParentID -> Blocks.AppendBlockChildren -> Notion m (ListOf BlockObject)+ DeleteBlock :: BlockID -> Notion m BlockObject+ -- Users+ RetrieveUser :: UserID -> Notion m UserObject+ ListUsers :: Maybe Natural -> Maybe Text -> Notion m (ListOf UserObject)+ RetrieveMyUser :: Notion m UserObject+ -- Search+ Search :: SearchRequest -> Notion m (ListOf PageOrDataSource)+ -- Comments+ CreateComment :: Comments.CreateComment -> Notion m CommentResponse+ ListComments :: Maybe BlockID -> Maybe Text -> Maybe Natural -> Notion m (ListOf CommentObject)+ RetrieveComment :: Comments.CommentID -> Notion m CommentResponse+ UpdateComment :: Comments.CommentID -> Comments.CommentContent -> Notion m CommentResponse+ DeleteComment :: Comments.CommentID -> Notion m CommentResponse+ -- Views+ CreateView :: Views.CreateView -> Notion m ViewObject+ RetrieveView :: Views.ViewID -> Notion m ViewObject+ UpdateView :: Views.ViewID -> Views.UpdateView -> Notion m ViewObject+ DeleteView :: Views.ViewID -> Notion m ViewObject+ ListViews ::+ Maybe UUID ->+ Maybe UUID ->+ Maybe Text ->+ Maybe Natural ->+ Notion m (ListOf ViewObject)+ CreateViewQuery :: Views.ViewID -> Views.CreateViewQuery -> Notion m Views.ViewQuery+ GetViewQueryResults :: Views.ViewID -> Views.ViewQueryID -> Maybe Text -> Maybe Natural -> Notion m (ListOf PartialPageObject)+ DeleteViewQuery :: Views.ViewID -> Views.ViewQueryID -> Notion m Views.DeletedViewQuery+ -- Custom Emojis+ ListCustomEmojis ::+ Maybe Text ->+ Maybe Text ->+ Maybe Natural ->+ Notion m (ListOf CustomEmoji)+ -- File Uploads+ CreateFileUpload :: FileUploads.CreateFileUpload -> Notion m FileUploadObject+ RetrieveFileUpload :: FileUploadID -> Notion m FileUploadObject+ SendFileUploadContent :: FileUploadID -> FileUploads.SendFileUpload -> Notion m FileUploadObject+ CompleteFileUpload :: FileUploadID -> Notion m FileUploadObject+ ListFileUploads ::+ Maybe FileUploadStatus ->+ Maybe Text ->+ Maybe Natural ->+ Notion m (ListOf FileUploadObject)+ -- Async Tasks+ RetrieveAsyncTask :: AsyncTaskID -> Notion m AsyncTask+ -- Meeting Notes+ CreateMeetingNote :: MeetingNotes.CreateMeetingNote -> Notion m MeetingNotes.CreateMeetingNoteResponse+ QueryMeetingNotes :: MeetingNotes.QueryMeetingNotes -> Notion m MeetingNotes.QueryMeetingNotesResponse++type instance DispatchOf Notion = 'Dynamic++-- ── Databases ─────────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.createDatabase'.+createDatabase :: (Notion :> es) => CreateDatabase -> Eff es DatabaseObject+createDatabase = send . CreateDatabase++-- | See 'Notion.V1.Methods'.'Notion.V1.retrieveDatabase'.+retrieveDatabase :: (Notion :> es) => DatabaseID -> Eff es DatabaseObject+retrieveDatabase = send . RetrieveDatabase++-- | See 'Notion.V1.Methods'.'Notion.V1.updateDatabase'.+updateDatabase :: (Notion :> es) => DatabaseID -> UpdateDatabase -> Eff es DatabaseObject+updateDatabase dbId upd = send (UpdateDatabase dbId upd)++{-# DEPRECATED queryDatabase "Use 'queryDataSource' instead." #-}++-- | See 'Notion.V1.Methods'.'Notion.V1.queryDatabase'. Deprecated upstream;+-- the constructor is retained for parity so existing call sites can migrate+-- in lockstep with the base library.+queryDatabase ::+ (Notion :> es) =>+ DatabaseID ->+ QueryDatabase ->+ Eff es (ListOf PageObject)+queryDatabase dbId q = send (QueryDatabase dbId q)++-- ── Data Sources ──────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.retrieveDataSource'.+retrieveDataSource :: (Notion :> es) => DataSourceID -> Eff es DataSourceObject+retrieveDataSource = send . RetrieveDataSource++-- | See 'Notion.V1.Methods'.'Notion.V1.createDataSource'.+createDataSource :: (Notion :> es) => DataSources.CreateDataSource -> Eff es DataSourceObject+createDataSource = send . CreateDataSource++-- | See 'Notion.V1.Methods'.'Notion.V1.updateDataSource'.+updateDataSource ::+ (Notion :> es) =>+ DataSourceID ->+ DataSources.UpdateDataSource ->+ Eff es DataSourceObject+updateDataSource dsId upd = send (UpdateDataSource dsId upd)++-- | See 'Notion.V1.Methods'.'Notion.V1.queryDataSource'.+queryDataSource ::+ (Notion :> es) =>+ DataSourceID ->+ DataSources.QueryDataSource ->+ Eff es (ListOf DataSources.PageOrDataSource)+queryDataSource dsId q = send (QueryDataSource dsId q)++-- | See 'Notion.V1.Methods'.'Notion.V1.listDataSourceTemplates'.+listDataSourceTemplates ::+ (Notion :> es) =>+ DataSourceID ->+ Maybe Text ->+ Maybe Text ->+ Maybe Natural ->+ Eff es DataSources.ListTemplatesResponse+listDataSourceTemplates dsId nameFilter startCursor pageSize =+ send (ListDataSourceTemplates dsId nameFilter startCursor pageSize)++-- ── Pages ─────────────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.createPage'.+createPage :: (Notion :> es) => CreatePage -> Eff es PageObject+createPage = send . CreatePage++-- | See 'Notion.V1.Methods'.'Notion.V1.createPageFiltered'.+createPageFiltered :: (Notion :> es) => [Text] -> CreatePage -> Eff es PageObject+createPageFiltered props req = send (CreatePageFiltered props req)++-- | See 'Notion.V1.Methods'.'Notion.V1.retrievePage'.+retrievePage :: (Notion :> es) => PageID -> Eff es PageObject+retrievePage = send . RetrievePage++-- | See 'Notion.V1.Methods'.'Notion.V1.retrievePageFiltered'.+retrievePageFiltered :: (Notion :> es) => PageID -> [Text] -> Eff es PageObject+retrievePageFiltered pid props = send (RetrievePageFiltered pid props)++-- | See 'Notion.V1.Methods'.'Notion.V1.updatePage'.+updatePage :: (Notion :> es) => PageID -> UpdatePage -> Eff es PageObject+updatePage pid upd = send (UpdatePage pid upd)++-- | See 'Notion.V1.Methods'.'Notion.V1.updatePageFiltered'.+updatePageFiltered :: (Notion :> es) => PageID -> [Text] -> UpdatePage -> Eff es PageObject+updatePageFiltered pid props upd = send (UpdatePageFiltered pid props upd)++-- | See 'Notion.V1.Methods'.'Notion.V1.retrievePageProperty'.+retrievePageProperty ::+ (Notion :> es) =>+ PageID ->+ Text ->+ Maybe Text ->+ Maybe Natural ->+ Eff es PropertyItemResponse+retrievePageProperty pid prop cursor size =+ send (RetrievePageProperty pid prop cursor size)++-- | See 'Notion.V1.Methods'.'Notion.V1.retrievePageMarkdown'.+retrievePageMarkdown :: (Notion :> es) => PageID -> Maybe Bool -> Eff es PageMarkdown+retrievePageMarkdown pid includeTx = send (RetrievePageMarkdown pid includeTx)++-- | See 'Notion.V1.Methods'.'Notion.V1.updatePageMarkdown'.+updatePageMarkdown :: (Notion :> es) => PageID -> UpdatePageMarkdown -> Eff es PageMarkdown+updatePageMarkdown pid upd = send (UpdatePageMarkdown pid upd)++-- | See 'Notion.V1.Methods'.'Notion.V1.movePage'.+movePage :: (Notion :> es) => PageID -> MovePage -> Eff es PageObject+movePage pid mv = send (MovePage pid mv)++-- | See 'Notion.V1.Methods'.'Notion.V1.createPageAsync'.+createPageAsync :: (Notion :> es) => CreatePage -> Eff es (AsyncOr PageObject)+createPageAsync = send . CreatePageAsync++-- | See 'Notion.V1.Methods'.'Notion.V1.updatePageMarkdownAsync'.+updatePageMarkdownAsync ::+ (Notion :> es) =>+ PageID ->+ UpdatePageMarkdown ->+ Eff es (AsyncOr PageMarkdown)+updatePageMarkdownAsync pid upd = send (UpdatePageMarkdownAsync pid upd)++-- ── Blocks ────────────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.retrieveBlock'.+retrieveBlock :: (Notion :> es) => BlockID -> Eff es BlockObject+retrieveBlock = send . RetrieveBlock++-- | See 'Notion.V1.Methods'.'Notion.V1.updateBlock'.+updateBlock :: (Notion :> es) => BlockID -> Blocks.BlockUpdatePayload -> Eff es BlockObject+updateBlock bid upd = send (UpdateBlock bid upd)++-- | See 'Notion.V1.Methods'.'Notion.V1.listBlockChildren'.+listBlockChildren ::+ (Notion :> es) =>+ ParentID ->+ Maybe Natural ->+ Maybe Text ->+ Eff es (ListOf BlockObject)+listBlockChildren pid pageSize startCursor =+ send (ListBlockChildren pid pageSize startCursor)++-- | See 'Notion.V1.Methods'.'Notion.V1.appendBlockChildren'.+appendBlockChildren ::+ (Notion :> es) =>+ ParentID ->+ Blocks.AppendBlockChildren ->+ Eff es (ListOf BlockObject)+appendBlockChildren pid append = send (AppendBlockChildren pid append)++-- | See 'Notion.V1.Methods'.'Notion.V1.deleteBlock'.+deleteBlock :: (Notion :> es) => BlockID -> Eff es BlockObject+deleteBlock = send . DeleteBlock++-- ── Users ─────────────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.retrieveUser'.+retrieveUser :: (Notion :> es) => UserID -> Eff es UserObject+retrieveUser = send . RetrieveUser++-- | See 'Notion.V1.Methods'.'Notion.V1.listUsers'.+listUsers :: (Notion :> es) => Maybe Natural -> Maybe Text -> Eff es (ListOf UserObject)+listUsers pageSize startCursor = send (ListUsers pageSize startCursor)++-- | See 'Notion.V1.Methods'.'Notion.V1.retrieveMyUser'.+retrieveMyUser :: (Notion :> es) => Eff es UserObject+retrieveMyUser = send RetrieveMyUser++-- ── Search ────────────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.search'.+search :: (Notion :> es) => SearchRequest -> Eff es (ListOf PageOrDataSource)+search = send . Search++-- ── Comments ──────────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.createComment'.+createComment :: (Notion :> es) => Comments.CreateComment -> Eff es CommentResponse+createComment = send . CreateComment++-- | See 'Notion.V1.Methods'.'Notion.V1.listComments'.+listComments ::+ (Notion :> es) =>+ Maybe BlockID ->+ Maybe Text ->+ Maybe Natural ->+ Eff es (ListOf CommentObject)+listComments bid startCursor pageSize = send (ListComments bid startCursor pageSize)++-- | See 'Notion.V1.Methods'.'Notion.V1.retrieveComment'.+retrieveComment :: (Notion :> es) => Comments.CommentID -> Eff es CommentResponse+retrieveComment = send . RetrieveComment++-- | See 'Notion.V1.Methods'.'Notion.V1.updateComment'.+updateComment ::+ (Notion :> es) =>+ Comments.CommentID ->+ Comments.CommentContent ->+ Eff es CommentResponse+updateComment cid content = send (UpdateComment cid content)++-- | See 'Notion.V1.Methods'.'Notion.V1.deleteComment'.+deleteComment :: (Notion :> es) => Comments.CommentID -> Eff es CommentResponse+deleteComment = send . DeleteComment++-- ── Views ─────────────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.createView'.+createView :: (Notion :> es) => Views.CreateView -> Eff es ViewObject+createView = send . CreateView++-- | See 'Notion.V1.Methods'.'Notion.V1.retrieveView'.+retrieveView :: (Notion :> es) => Views.ViewID -> Eff es ViewObject+retrieveView = send . RetrieveView++-- | See 'Notion.V1.Methods'.'Notion.V1.updateView'.+updateView :: (Notion :> es) => Views.ViewID -> Views.UpdateView -> Eff es ViewObject+updateView vid upd = send (UpdateView vid upd)++-- | See 'Notion.V1.Methods'.'Notion.V1.deleteView'.+deleteView :: (Notion :> es) => Views.ViewID -> Eff es ViewObject+deleteView = send . DeleteView++-- | See 'Notion.V1.Methods'.'Notion.V1.listViews'.+listViews ::+ (Notion :> es) =>+ Maybe UUID ->+ Maybe UUID ->+ Maybe Text ->+ Maybe Natural ->+ Eff es (ListOf ViewObject)+listViews dbId dsId startCursor pageSize =+ send (ListViews dbId dsId startCursor pageSize)++-- | See 'Notion.V1.Methods'.'Notion.V1.createViewQuery'.+createViewQuery :: (Notion :> es) => Views.ViewID -> Views.CreateViewQuery -> Eff es Views.ViewQuery+createViewQuery vid req = send (CreateViewQuery vid req)++-- | See 'Notion.V1.Methods'.'Notion.V1.getViewQueryResults'.+getViewQueryResults ::+ (Notion :> es) =>+ Views.ViewID ->+ Views.ViewQueryID ->+ Maybe Text ->+ Maybe Natural ->+ Eff es (ListOf PartialPageObject)+getViewQueryResults vid qid cursor size = send (GetViewQueryResults vid qid cursor size)++-- | See 'Notion.V1.Methods'.'Notion.V1.deleteViewQuery'.+deleteViewQuery :: (Notion :> es) => Views.ViewID -> Views.ViewQueryID -> Eff es Views.DeletedViewQuery+deleteViewQuery vid qid = send (DeleteViewQuery vid qid)++-- ── Custom Emojis ─────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.listCustomEmojis'.+listCustomEmojis ::+ (Notion :> es) =>+ Maybe Text ->+ Maybe Text ->+ Maybe Natural ->+ Eff es (ListOf CustomEmoji)+listCustomEmojis nameFilter startCursor pageSize =+ send (ListCustomEmojis nameFilter startCursor pageSize)++-- ── File Uploads ──────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.createFileUpload'.+createFileUpload :: (Notion :> es) => FileUploads.CreateFileUpload -> Eff es FileUploadObject+createFileUpload = send . CreateFileUpload++-- | See 'Notion.V1.Methods'.'Notion.V1.retrieveFileUpload'.+retrieveFileUpload :: (Notion :> es) => FileUploadID -> Eff es FileUploadObject+retrieveFileUpload = send . RetrieveFileUpload++-- | See 'Notion.V1.Methods'.'Notion.V1.sendFileUploadContent'.+sendFileUploadContent ::+ (Notion :> es) =>+ FileUploadID ->+ FileUploads.SendFileUpload ->+ Eff es FileUploadObject+sendFileUploadContent fid payload = send (SendFileUploadContent fid payload)++-- | See 'Notion.V1.Methods'.'Notion.V1.completeFileUpload'.+completeFileUpload :: (Notion :> es) => FileUploadID -> Eff es FileUploadObject+completeFileUpload = send . CompleteFileUpload++-- | See 'Notion.V1.Methods'.'Notion.V1.listFileUploads'.+listFileUploads ::+ (Notion :> es) =>+ Maybe FileUploadStatus ->+ Maybe Text ->+ Maybe Natural ->+ Eff es (ListOf FileUploadObject)+listFileUploads statusFilter startCursor pageSize =+ send (ListFileUploads statusFilter startCursor pageSize)++-- ── Async Tasks ───────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.retrieveAsyncTask'. Pass it to+-- 'Notion.V1.AsyncTasks.waitForAsyncTask' to poll from 'Eff'.+retrieveAsyncTask :: (Notion :> es) => AsyncTaskID -> Eff es AsyncTask+retrieveAsyncTask = send . RetrieveAsyncTask++-- ── Meeting Notes ─────────────────────────────────────────────────++-- | See 'Notion.V1.Methods'.'Notion.V1.createMeetingNote'.+createMeetingNote ::+ (Notion :> es) =>+ MeetingNotes.CreateMeetingNote ->+ Eff es MeetingNotes.CreateMeetingNoteResponse+createMeetingNote = send . CreateMeetingNote++-- | See 'Notion.V1.Methods'.'Notion.V1.queryMeetingNotes'.+queryMeetingNotes ::+ (Notion :> es) =>+ MeetingNotes.QueryMeetingNotes ->+ Eff es MeetingNotes.QueryMeetingNotesResponse+queryMeetingNotes = send . QueryMeetingNotes
+ src/Notion/V1/Effectful/Interpreter.hs view
@@ -0,0 +1,179 @@+-- | Default interpreter for the 'Notion' effect.+--+-- 'runNotion' dispatches every 'Notion' constructor through a+-- concrete 'Notion.V1.Methods' value. Any 'NotionError' thrown by+-- the underlying 'IO' action is caught and re-raised via the+-- 'Error' effect, so callers can branch on Notion API error shapes+-- (e.g. @object_not_found@) without resorting to 'IO'-level exception+-- handling. Other 'Servant.Client.ClientError' values (network+-- failures, decoding errors) are intentionally /not/ caught: they+-- remain 'IO' exceptions, preserving the existing 'Notion.V1' contract.+-- The same holds for 'Notion.V1.Error.UnknownHTTPResponseError',+-- 'Notion.V1.Error.RequestTimeoutError' and+-- 'Notion.V1.Error.InvalidPathParameterError'. Retries happen inside the+-- 'Notion.V1.Methods' value, before an error reaches the interpreter.+module Notion.V1.Effectful.Interpreter+ ( runNotion,+ )+where++import Control.Exception qualified as Exception+import Effectful (Eff, IOE, liftIO, (:>))+import Effectful.Dispatch.Dynamic (interpret)+import Effectful.Error.Static (Error, throwError)+import Notion.V1 (Methods)+import Notion.V1 qualified as Notion+import Notion.V1.Effectful.Effect+ ( Notion+ ( AppendBlockChildren,+ CompleteFileUpload,+ CreateComment,+ CreateDataSource,+ CreateDatabase,+ CreateFileUpload,+ CreateMeetingNote,+ CreatePage,+ CreatePageAsync,+ CreatePageFiltered,+ CreateView,+ CreateViewQuery,+ DeleteBlock,+ DeleteComment,+ DeleteView,+ DeleteViewQuery,+ GetViewQueryResults,+ ListBlockChildren,+ ListComments,+ ListCustomEmojis,+ ListDataSourceTemplates,+ ListFileUploads,+ ListUsers,+ ListViews,+ MovePage,+ QueryDataSource,+ QueryDatabase,+ QueryMeetingNotes,+ RetrieveAsyncTask,+ RetrieveBlock,+ RetrieveComment,+ RetrieveDataSource,+ RetrieveDatabase,+ RetrieveFileUpload,+ RetrieveMyUser,+ RetrievePage,+ RetrievePageFiltered,+ RetrievePageMarkdown,+ RetrievePageProperty,+ RetrieveUser,+ RetrieveView,+ Search,+ SendFileUploadContent,+ UpdateBlock,+ UpdateComment,+ UpdateDataSource,+ UpdateDatabase,+ UpdatePage,+ UpdatePageFiltered,+ UpdatePageMarkdown,+ UpdatePageMarkdownAsync,+ UpdateView+ ),+ )+import Notion.V1.Error (NotionError)++-- | Interpret 'Notion' using a concrete 'Methods' value.+--+-- >>> runEff . runErrorNoCallStack @NotionError . runNotion methods $ retrievePage pid+--+-- The dispatched 'IO' action is wrapped with 'Control.Exception.try':+-- a 'NotionError' is re-thrown via 'throwError', anything else+-- propagates as an 'IO' exception.+runNotion ::+ (IOE :> es, Error NotionError :> es) =>+ Methods ->+ Eff (Notion : es) a ->+ Eff es a+runNotion methods = interpret $ \_ -> \case+ -- Databases+ CreateDatabase req -> runIO (Notion.createDatabase methods req)+ RetrieveDatabase dbId -> runIO (Notion.retrieveDatabase methods dbId)+ UpdateDatabase dbId req -> runIO (Notion.updateDatabase methods dbId req)+ QueryDatabase dbId req -> runIO (Notion.queryDatabase methods dbId req)+ -- Data Sources+ RetrieveDataSource dsId -> runIO (Notion.retrieveDataSource methods dsId)+ CreateDataSource req -> runIO (Notion.createDataSource methods req)+ UpdateDataSource dsId req -> runIO (Notion.updateDataSource methods dsId req)+ QueryDataSource dsId req -> runIO (Notion.queryDataSource methods dsId req)+ ListDataSourceTemplates dsId nameFilter cursor pageSize ->+ runIO (Notion.listDataSourceTemplates methods dsId nameFilter cursor pageSize)+ -- Pages+ CreatePage req -> runIO (Notion.createPage methods req)+ CreatePageFiltered props req -> runIO (Notion.createPageFiltered methods props req)+ RetrievePage pid -> runIO (Notion.retrievePage methods pid)+ RetrievePageFiltered pid props -> runIO (Notion.retrievePageFiltered methods pid props)+ UpdatePage pid req -> runIO (Notion.updatePage methods pid req)+ UpdatePageFiltered pid props req -> runIO (Notion.updatePageFiltered methods pid props req)+ RetrievePageProperty pid prop cursor size ->+ runIO (Notion.retrievePageProperty methods pid prop cursor size)+ RetrievePageMarkdown pid includeTx ->+ runIO (Notion.retrievePageMarkdown methods pid includeTx)+ UpdatePageMarkdown pid req -> runIO (Notion.updatePageMarkdown methods pid req)+ MovePage pid req -> runIO (Notion.movePage methods pid req)+ CreatePageAsync req -> runIO (Notion.createPageAsync methods req)+ UpdatePageMarkdownAsync pid req -> runIO (Notion.updatePageMarkdownAsync methods pid req)+ -- Blocks+ RetrieveBlock bid -> runIO (Notion.retrieveBlock methods bid)+ UpdateBlock bid req -> runIO (Notion.updateBlock methods bid req)+ ListBlockChildren pid pageSize cursor ->+ runIO (Notion.listBlockChildren methods pid pageSize cursor)+ AppendBlockChildren pid req -> runIO (Notion.appendBlockChildren methods pid req)+ DeleteBlock bid -> runIO (Notion.deleteBlock methods bid)+ -- Users+ RetrieveUser uid -> runIO (Notion.retrieveUser methods uid)+ ListUsers pageSize cursor -> runIO (Notion.listUsers methods pageSize cursor)+ RetrieveMyUser -> runIO (Notion.retrieveMyUser methods)+ -- Search+ Search req -> runIO (Notion.search methods req)+ -- Comments+ CreateComment req -> runIO (Notion.createComment methods req)+ ListComments bid cursor pageSize ->+ runIO (Notion.listComments methods bid cursor pageSize)+ RetrieveComment cid -> runIO (Notion.retrieveComment methods cid)+ UpdateComment cid content -> runIO (Notion.updateComment methods cid content)+ DeleteComment cid -> runIO (Notion.deleteComment methods cid)+ -- Views+ CreateView req -> runIO (Notion.createView methods req)+ RetrieveView vid -> runIO (Notion.retrieveView methods vid)+ UpdateView vid req -> runIO (Notion.updateView methods vid req)+ DeleteView vid -> runIO (Notion.deleteView methods vid)+ ListViews dbId dsId cursor pageSize ->+ runIO (Notion.listViews methods dbId dsId cursor pageSize)+ CreateViewQuery vid req -> runIO (Notion.createViewQuery methods vid req)+ GetViewQueryResults vid qid cursor size ->+ runIO (Notion.getViewQueryResults methods vid qid cursor size)+ DeleteViewQuery vid qid -> runIO (Notion.deleteViewQuery methods vid qid)+ -- Custom Emojis+ ListCustomEmojis nameFilter cursor pageSize ->+ runIO (Notion.listCustomEmojis methods nameFilter cursor pageSize)+ -- File Uploads+ CreateFileUpload req -> runIO (Notion.createFileUpload methods req)+ RetrieveFileUpload fid -> runIO (Notion.retrieveFileUpload methods fid)+ SendFileUploadContent fid payload ->+ runIO (Notion.sendFileUploadContent methods fid payload)+ CompleteFileUpload fid -> runIO (Notion.completeFileUpload methods fid)+ ListFileUploads statusFilter cursor pageSize ->+ runIO (Notion.listFileUploads methods statusFilter cursor pageSize)+ -- Async Tasks+ RetrieveAsyncTask tid -> runIO (Notion.retrieveAsyncTask methods tid)+ -- Meeting Notes+ CreateMeetingNote req -> runIO (Notion.createMeetingNote methods req)+ QueryMeetingNotes req -> runIO (Notion.queryMeetingNotes methods req)++-- | Run an 'IO' action, funneling any thrown 'NotionError' through+-- the 'Error' effect.+runIO :: (IOE :> es, Error NotionError :> es) => IO a -> Eff es a+runIO action = do+ result <- liftIO (Exception.try action)+ case result of+ Left (ne :: NotionError) -> throwError ne+ Right a -> pure a