packages feed

notion-client-0.5.0.0: src/Notion/V1/Databases.hs

-- | @\/v1\/databases@
module Notion.V1.Databases
  ( -- * Main types
    DatabaseID,
    DatabaseObject (..),
    DataSource (..),
    InitialDataSource (..),
    CreateDatabase (..),
    UpdateDatabase (..),
    QueryDatabase (..),

    -- * Servant
    API,
  )
where

import Control.Applicative ((<|>))
import Data.Aeson ((.:), (.:?))
import Notion.Prelude
import Notion.V1.Common (Cover, Icon, ObjectType (..), Parent, UUID)
import Notion.V1.Filter (Filter, Sort)
import Notion.V1.ListOf (ListOf)
import Notion.V1.Pages (PageObject)
import Notion.V1.Properties (PropertySchema)
import Notion.V1.RichText (RichText)
import Notion.V1.Users (UserReference)
import Prelude hiding (id)

-- | Database ID
type DatabaseID = UUID

-- | Data source reference within a database (API version 2025-09-03+)
data DataSource = DataSource
  { id :: UUID,
    name :: Text
  }
  deriving stock (Generic, Show)

instance FromJSON DataSource where
  parseJSON = genericParseJSON aesonOptions

-- | Notion database object
--
-- In API version 2025-09-03, database schema (properties) moved to data sources.
-- The 'properties' field may be absent; use 'dataSources' and the data source
-- endpoints to access schema information.
data DatabaseObject = DatabaseObject
  { id :: DatabaseID,
    createdTime :: POSIXTime,
    lastEditedTime :: POSIXTime,
    createdBy :: Maybe UserReference,
    lastEditedBy :: Maybe UserReference,
    title :: Vector RichText,
    description :: Maybe (Vector RichText),
    properties :: Maybe (Map Text PropertySchema),
    icon :: Maybe Icon,
    cover :: Maybe Cover,
    url :: Text,
    parent :: Parent,
    isInline :: Maybe Bool,
    inTrash :: Maybe Bool,
    isLocked :: Maybe Bool,
    publicUrl :: Maybe Text,
    dataSources :: Vector DataSource,
    object :: ObjectType
  }
  deriving stock (Generic, Show)

instance FromJSON DatabaseObject where
  parseJSON = \case
    Object o -> do
      id <- o .: "id"
      createdTimeStr <- o .: "created_time"
      createdTime <- parseISO8601 createdTimeStr
      lastEditedTimeStr <- o .: "last_edited_time"
      lastEditedTime <- parseISO8601 lastEditedTimeStr
      createdBy <- o .:? "created_by"
      lastEditedBy <- o .:? "last_edited_by"
      title <- o .: "title"
      description <- o .:? "description"
      properties <- o .:? "properties"
      icon <- o .:? "icon"
      cover <- o .:? "cover"
      url <- o .: "url"
      parent <- o .: "parent"
      isInline <- o .:? "is_inline"
      inTrash <- (fmap Just (o .: "in_trash")) <|> (fmap Just (o .: "is_archived")) <|> (fmap Just (o .: "archived")) <|> pure Nothing
      isLocked <- o .:? "is_locked"
      publicUrl <- o .:? "public_url"
      dataSources <- o .: "data_sources"
      object <- o .: "object"
      return DatabaseObject {..}
    _ -> fail "Expected object for DatabaseObject"

-- | Initial data source configuration for database creation.
-- Contains the property schema for the database's first data source.
newtype InitialDataSource = InitialDataSource
  { properties :: Map Text PropertySchema
  }
  deriving stock (Generic, Show)

instance ToJSON InitialDataSource where
  toJSON = genericToJSON aesonOptions

-- | Create database request
--
-- In API version 2025-09-03, schema is specified via 'initialDataSource'
-- rather than a top-level @properties@ field.
data CreateDatabase = CreateDatabase
  { parent :: Parent,
    title :: Vector RichText,
    initialDataSource :: Maybe InitialDataSource,
    icon :: Maybe Icon,
    cover :: Maybe Cover,
    description :: Maybe (Vector RichText),
    isInline :: Maybe Bool
  }
  deriving stock (Generic, Show)

instance ToJSON CreateDatabase where
  toJSON = genericToJSON aesonOptions

-- | Update database request
--
-- In API version 2025-09-03, schema updates (properties) are handled via
-- the Update Data Source API ('Notion.V1.DataSources.UpdateDataSource').
-- This endpoint only handles database-level attributes.
data UpdateDatabase = UpdateDatabase
  { title :: Maybe (Vector RichText),
    icon :: Maybe Icon,
    cover :: Maybe Cover,
    description :: Maybe (Vector RichText),
    isInline :: Maybe Bool,
    isLocked :: Maybe Bool,
    inTrash :: Maybe Bool,
    parent :: Maybe Parent
  }
  deriving stock (Generic, Show)

instance ToJSON UpdateDatabase where
  toJSON = genericToJSON aesonOptions

-- | Query database request
data QueryDatabase = QueryDatabase
  { filter :: Maybe Filter,
    sorts :: Maybe [Sort],
    startCursor :: Maybe Text,
    pageSize :: Maybe Natural,
    -- | Limit which properties are returned in the response.
    -- Each element is a property ID (not name).
    filterProperties :: Maybe [Text]
  }
  deriving stock (Generic, Show)

instance ToJSON QueryDatabase where
  toJSON = genericToJSON aesonOptions

-- | Servant API
type API =
  "databases"
    :> ( ReqBody '[JSON] CreateDatabase
           :> Post '[JSON] DatabaseObject
           :<|> Capture "database_id" DatabaseID
           :> Get '[JSON] DatabaseObject
           :<|> Capture "database_id" DatabaseID
           :> ReqBody '[JSON] UpdateDatabase
           :> Patch '[JSON] DatabaseObject
           :<|> Capture "database_id" DatabaseID
           :> "query"
           :> ReqBody '[JSON] QueryDatabase
           :> Post '[JSON] (ListOf PageObject)
       )