packages feed

notion-client-0.8.0.0: src/Notion/V1.hs

-- | Notion API v1
--
-- Example usage:
--
-- @
-- module Main where
--
-- import Notion.V1
-- import Notion.V1.Pages
-- import Data.Text qualified as Text
-- import System.Environment qualified as Environment
--
-- main :: IO ()
-- main = do
--     token <- Environment.getEnv "NOTION_TOKEN"
--
--     manager <- newTlsManager
--
--     let methods = makeMethodsWith defaultClientConfig manager (Text.pack token)
--
--     page <- retrievePage methods "page-id-here"
--
--     print page
-- @
module Notion.V1
  ( -- * Methods
    getClientEnv,
    makeMethods,
    makeMethodsWith,
    makeMethodsWithEnv,
    Methods (..),

    -- * Configuration
    ClientConfig (..),
    defaultClientConfig,
    legacyClientConfig,
    defaultBaseUrl,
    defaultNotionVersion,
    RetryOptions (..),
    defaultRetryOptions,
    noRetries,
    LogLevel (..),
    Logger,
    stderrLogger,

    -- * Runtime building blocks
    RequestContext (..),
    requestContextFor,
    standardHeaders,
    responseTimeoutFor,
    withRetries,

    -- * Servant
    API,
  )
where

import Data.Maybe (fromMaybe)
import Data.Proxy (Proxy (..))
import Data.Text qualified as Text
import Network.HTTP.Client (Manager)
import Network.HTTP.Client.TLS qualified as TLS
import Notion.Prelude
import Notion.V1.AsyncTasks (AllowAsync (..), AsyncOr, AsyncTask, AsyncTaskID, fromAsyncUnion)
import Notion.V1.AsyncTasks qualified as AsyncTasks
import Notion.V1.Blocks (BlockID, BlockObject)
import Notion.V1.Blocks qualified as Blocks
import Notion.V1.Client
  ( ClientConfig (..),
    LogLevel (..),
    Logger,
    RequestContext (..),
    RetryOptions (..),
    configureClientEnv,
    defaultBaseUrl,
    defaultClientConfig,
    defaultNotionVersion,
    defaultRetryOptions,
    legacyClientConfig,
    noRetries,
    requestContextFor,
    responseTimeoutFor,
    runClientWith,
    standardHeaders,
    stderrLogger,
    withRetries,
  )
import Notion.V1.Comments (CommentContent, CommentObject, CommentResponse)
import Notion.V1.Comments qualified as Comments
import Notion.V1.Common (ParentID, UUID)
import Notion.V1.CustomEmojis (CustomEmoji)
import Notion.V1.CustomEmojis qualified as CustomEmojis
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.Databases qualified as Databases
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.Pages qualified as Pages
import Notion.V1.Search (SearchRequest)
import Notion.V1.Search qualified as Search
import Notion.V1.Users (UserID, UserObject)
import Notion.V1.Users qualified as Users
import Notion.V1.Views (ViewObject)
import Notion.V1.Views qualified as Views
import Servant.Client (ClientEnv)
import Servant.Client qualified as Client
import Servant.Multipart.Client (genBoundary)

-- | Convenient utility to get a `ClientEnv` for the most common use case
getClientEnv ::
  -- | Base URL for API
  Text ->
  IO ClientEnv
getClientEnv baseUrlText = do
  baseUrl <- Client.parseBaseUrl (Text.unpack baseUrlText)
  manager <- TLS.newTlsManager
  pure (Client.mkClientEnv manager baseUrl)

-- | Get a record of API methods after providing an API token.
--
-- Uses 'legacyClientConfig': default API version, retries and @User-Agent@,
-- keeping the 'ClientEnv' manager's own timeout.
makeMethods ::
  ClientEnv ->
  -- | API token
  Text ->
  Methods
makeMethods = makeMethodsWithEnv legacyClientConfig

-- | Build 'Methods' from a configuration, a connection manager (for example
-- from 'Network.HTTP.Client.TLS.newTlsManager') and an API token. The base URL
-- comes from 'apiBaseUrl'.
makeMethodsWith :: ClientConfig -> Manager -> Text -> Methods
makeMethodsWith config manager =
  makeMethodsWithEnv config (Client.mkClientEnv manager (apiBaseUrl config))

-- | Build 'Methods' from a configuration, an existing 'ClientEnv' (which
-- supplies the manager and base URL) and an API token.
makeMethodsWithEnv ::
  ClientConfig ->
  ClientEnv ->
  -- | API token
  Text ->
  Methods
makeMethodsWithEnv config clientEnv token = Methods {..}
  where
    context = requestContextFor config clientEnv token
    configuredEnv = configureClientEnv config clientEnv
    ( ( createDatabase
          :<|> retrieveDatabase
          :<|> updateDatabase
          :<|> queryDatabase_
        )
        :<|> ( retrieveDataSource
                 :<|> createDataSource
                 :<|> updateDataSource
                 :<|> queryDataSource_
                 :<|> listDataSourceTemplates_
               )
        :<|> ( retrievePageFiltered
                 :<|> createPageFiltered
                 :<|> updatePageFiltered
                 :<|> retrievePageProperty
                 :<|> retrievePageMarkdown
                 :<|> updatePageMarkdown
                 :<|> movePage
                 :<|> createPageAsync_
                 :<|> updatePageMarkdownAsync_
               )
        :<|> ( retrieveBlock
                 :<|> updateBlock
                 :<|> retrieveBlockChildren_
                 :<|> appendBlockChildren
                 :<|> deleteBlock
               )
        :<|> ( retrieveUser
                 :<|> listUsers_
                 :<|> retrieveMyUser
               )
        :<|> search_
        :<|> ( createComment
                 :<|> listComments_
                 :<|> retrieveComment
                 :<|> updateComment
                 :<|> deleteComment
               )
        :<|> ( createView
                 :<|> retrieveView
                 :<|> updateView
                 :<|> deleteView
                 :<|> listViews_
                 :<|> createViewQuery
                 :<|> getViewQueryResults_
                 :<|> deleteViewQuery
               )
        :<|> listCustomEmojis_
        :<|> ( createFileUpload
                 :<|> retrieveFileUpload
                 :<|> sendFileUploadContent_
                 :<|> completeFileUpload
                 :<|> listFileUploads_
               )
        :<|> retrieveAsyncTask
        :<|> ( createMeetingNote
                 :<|> queryMeetingNotes
               )
      ) =
        Client.hoistClient
          @API
          Proxy
          run
          (Client.client @API Proxy)
          (contextAuthorization context)
          (notionVersion config)

    run :: Client.ClientM a -> IO a
    run = runClientWith configuredEnv

    -- Wrap retrievePageFiltered to provide backward-compatible retrievePage
    retrievePage pid = retrievePageFiltered pid []
    createPage = createPageFiltered []
    updatePage pid = updatePageFiltered pid []

    -- The async variants always send allow_async: true
    createPageAsync req = fromAsyncUnion <$> createPageAsync_ (AllowAsync req)
    updatePageMarkdownAsync pid req = fromAsyncUnion <$> updatePageMarkdownAsync_ pid (AllowAsync req)

    -- filter_properties is sent as repeated query parameters (see DataSources.API)
    queryDataSource dsId q@DataSources.QueryDataSource {filterProperties = props} =
      queryDataSource_ dsId (fromMaybe [] props) q
    queryDatabase dbId q@Databases.QueryDatabase {filterProperties = props} =
      queryDatabase_ dbId (fromMaybe [] props) q

    -- Keep the ListOf structure
    listBlockChildren = retrieveBlockChildren_
    listUsers = listUsers_
    listComments = listComments_
    search = search_
    listDataSourceTemplates = listDataSourceTemplates_
    listViews = listViews_
    getViewQueryResults = getViewQueryResults_
    listCustomEmojis = listCustomEmojis_
    listFileUploads = listFileUploads_
    sendFileUploadContent fid upload = do
      boundary <- genBoundary
      sendFileUploadContent_ fid (boundary, upload)

-- | API methods
data Methods = Methods
  { -- \* Databases
    createDatabase :: CreateDatabase -> IO DatabaseObject,
    retrieveDatabase :: DatabaseID -> IO DatabaseObject,
    updateDatabase :: DatabaseID -> UpdateDatabase -> IO DatabaseObject,
    -- | @Deprecated: Use 'queryDataSource' instead.@
    queryDatabase :: DatabaseID -> QueryDatabase -> IO (ListOf PageObject),
    -- \* Data Sources
    retrieveDataSource :: DataSourceID -> IO DataSourceObject,
    createDataSource :: DataSources.CreateDataSource -> IO DataSourceObject,
    updateDataSource :: DataSourceID -> DataSources.UpdateDataSource -> IO DataSourceObject,
    queryDataSource :: DataSourceID -> DataSources.QueryDataSource -> IO (ListOf DataSources.PageOrDataSource),
    -- | List templates available for a data source
    listDataSourceTemplates ::
      DataSourceID ->
      Maybe Text ->
      -- \^ name filter (exact match)
      Maybe Text ->
      -- \^ start_cursor
      Maybe Natural ->
      -- \^ page_size
      IO DataSources.ListTemplatesResponse,
    -- \* Pages
    createPage :: CreatePage -> IO PageObject,
    -- | Create a page, limiting which properties the response includes.
    createPageFiltered :: [Text] -> CreatePage -> IO PageObject,
    retrievePage :: PageID -> IO PageObject,
    -- | Retrieve a page, optionally filtering which properties are returned.
    retrievePageFiltered :: PageID -> [Text] -> IO PageObject,
    updatePage :: PageID -> UpdatePage -> IO PageObject,
    -- | Update a page, limiting which properties the response includes.
    updatePageFiltered :: PageID -> [Text] -> UpdatePage -> IO PageObject,
    -- | Retrieve a single page property item.
    -- For title, rich_text, relation, and people properties, the response may be paginated.
    retrievePageProperty ::
      PageID ->
      Text ->
      -- \^ property_id
      Maybe Text ->
      -- \^ start_cursor
      Maybe Natural ->
      -- \^ page_size
      IO PropertyItemResponse,
    retrievePageMarkdown ::
      PageID ->
      Maybe Bool ->
      -- \^ include_transcript
      IO PageMarkdown,
    -- | Update page content using markdown. Supports targeted search-and-replace
    -- edits, full content replacement, and legacy insert/replace commands.
    updatePageMarkdown ::
      PageID ->
      UpdatePageMarkdown ->
      IO PageMarkdown,
    -- | Move a page to a new parent (page or data source)
    movePage ::
      PageID ->
      MovePage ->
      IO PageObject,
    -- | Like 'createPage' but sends @allow_async: true@, so Notion may answer
    -- with an async task instead of the page. Only meaningful when the
    -- request's @markdown@ is set.
    createPageAsync :: CreatePage -> IO (AsyncOr PageObject),
    -- | Like 'updatePageMarkdown' but sends @allow_async: true@, so Notion may
    -- answer with an async task instead of the result.
    updatePageMarkdownAsync :: PageID -> UpdatePageMarkdown -> IO (AsyncOr PageMarkdown),
    -- \* Blocks
    retrieveBlock :: BlockID -> IO BlockObject,
    -- | Update part of a block, or trash it with 'Blocks.trashBlockUpdate'.
    updateBlock :: BlockID -> Blocks.BlockUpdatePayload -> IO BlockObject,
    listBlockChildren ::
      ParentID ->
      Maybe Natural ->
      -- \^ page_size
      Maybe Text ->
      -- \^ start_cursor
      IO (ListOf BlockObject),
    appendBlockChildren :: ParentID -> Blocks.AppendBlockChildren -> IO (ListOf BlockObject),
    deleteBlock :: BlockID -> IO BlockObject,
    -- \* Users
    retrieveUser :: UserID -> IO UserObject,
    listUsers ::
      Maybe Natural ->
      -- \^ page_size
      Maybe Text ->
      -- \^ start_cursor
      IO (ListOf UserObject),
    retrieveMyUser :: IO UserObject,
    -- \* Search
    search :: SearchRequest -> IO (ListOf Search.PageOrDataSource),
    -- \* Comments

    -- | Create a comment on a page or block, or a reply in a discussion.
    createComment :: Comments.CreateComment -> IO CommentResponse,
    -- | List comments on a block or page. To list comments on a page, use the page ID
    -- as the block_id parameter (pages are blocks in Notion).
    listComments ::
      Maybe BlockID ->
      -- \^ block_id (use page ID here for page comments)
      Maybe Text ->
      -- \^ start_cursor
      Maybe Natural ->
      -- \^ page_size
      IO (ListOf CommentObject),
    retrieveComment :: Comments.CommentID -> IO CommentResponse,
    -- | Replace a comment's content with rich text or Markdown.
    updateComment :: Comments.CommentID -> CommentContent -> IO CommentResponse,
    deleteComment :: Comments.CommentID -> IO CommentResponse,
    -- \* Views
    createView :: Views.CreateView -> IO ViewObject,
    retrieveView :: Views.ViewID -> IO ViewObject,
    updateView :: Views.ViewID -> Views.UpdateView -> IO ViewObject,
    deleteView :: Views.ViewID -> IO ViewObject,
    listViews ::
      Maybe UUID ->
      -- \^ database_id
      Maybe UUID ->
      -- \^ data_source_id
      Maybe Text ->
      -- \^ start_cursor
      Maybe Natural ->
      -- \^ page_size
      IO (ListOf ViewObject),
    -- | Create a view query: a short-lived snapshot of the rows a view shows,
    -- with its first page of results.
    createViewQuery :: Views.ViewID -> Views.CreateViewQuery -> IO Views.ViewQuery,
    -- | Page through a view query's results.
    getViewQueryResults ::
      Views.ViewID ->
      Views.ViewQueryID ->
      Maybe Text ->
      -- \^ start_cursor
      Maybe Natural ->
      -- \^ page_size
      IO (ListOf PartialPageObject),
    -- | Delete a view query before it expires.
    deleteViewQuery :: Views.ViewID -> Views.ViewQueryID -> IO Views.DeletedViewQuery,
    -- \* Custom Emojis
    listCustomEmojis ::
      Maybe Text ->
      -- \^ name filter (exact match)
      Maybe Text ->
      -- \^ start_cursor
      Maybe Natural ->
      -- \^ page_size
      IO (ListOf CustomEmoji),
    -- \* File Uploads
    createFileUpload :: FileUploads.CreateFileUpload -> IO FileUploadObject,
    retrieveFileUpload :: FileUploadID -> IO FileUploadObject,
    sendFileUploadContent :: FileUploadID -> FileUploads.SendFileUpload -> IO FileUploadObject,
    completeFileUpload :: FileUploadID -> IO FileUploadObject,
    listFileUploads ::
      Maybe FileUploadStatus ->
      -- \^ status filter
      Maybe Text ->
      -- \^ start_cursor
      Maybe Natural ->
      -- \^ page_size
      IO (ListOf FileUploadObject),
    -- \* Async tasks

    -- | Retrieve a background task; see 'Notion.V1.AsyncTasks.waitForAsyncTask'.
    retrieveAsyncTask :: AsyncTaskID -> IO AsyncTask,
    -- \* Meeting notes

    -- | Create a meeting note from an uploaded recording or an existing media block.
    createMeetingNote :: MeetingNotes.CreateMeetingNote -> IO MeetingNotes.CreateMeetingNoteResponse,
    -- | Query meeting notes with a filter, sort and limit. Not paginated.
    queryMeetingNotes :: MeetingNotes.QueryMeetingNotes -> IO MeetingNotes.QueryMeetingNotesResponse
  }

-- | Servant API
type API =
  Header' [Required, Strict] "Authorization" Text
    :> Header' [Required, Strict] "Notion-Version" Text
    :> ( Databases.API
           :<|> DataSources.API
           :<|> Pages.API
           :<|> Blocks.API
           :<|> Users.API
           :<|> Search.API
           :<|> Comments.API
           :<|> Views.API
           :<|> CustomEmojis.API
           :<|> FileUploads.API
           :<|> AsyncTasks.API
           :<|> MeetingNotes.API
       )