packages feed

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

-- | @\/v1\/search@
module Notion.V1.Search
  ( -- * Main types
    SearchRequest (..),
    _SearchRequest,
    SearchSortDirection (..),
    SearchSort (..),
    SearchFilter (..),
    SearchObjectType (..),

    -- * Results
    SearchResult,
    PageOrDataSource (..),
    PartialPageObject (..),
    PartialDataSourceObject (..),
    pageResults,
    dataSourceResults,

    -- * Convenience constructors
    pageFilter,
    dataSourceFilter,

    -- * Servant
    API,
  )
where

import Data.Aeson ((.=))
import Data.Aeson qualified as Aeson
import Notion.Prelude
import Notion.V1.DataSources (PageOrDataSource (..), PartialDataSourceObject (..), PartialPageObject (..), dataSourceResults, pageResults)
import Notion.V1.ListOf (ListOf)

-- | Search request
data SearchRequest = SearchRequest
  { query :: Maybe Text,
    sort :: Maybe SearchSort,
    filter :: Maybe SearchFilter,
    startCursor :: Maybe Text,
    pageSize :: Maybe Natural
  }
  deriving stock (Generic, Show)

instance ToJSON SearchRequest where
  toJSON = genericToJSON aesonOptions

-- | Default search request
_SearchRequest :: SearchRequest
_SearchRequest =
  SearchRequest
    { query = Nothing,
      sort = Nothing,
      filter = Nothing,
      startCursor = Nothing,
      pageSize = Nothing
    }

-- | Search sort direction
data SearchSortDirection
  = Ascending
  | Descending
  deriving stock (Generic, Show)

instance ToJSON SearchSortDirection where
  toJSON = genericToJSON aesonOptions

-- | Search sort
data SearchSort
  = -- | @{"timestamp":"last_edited_time","direction":...}@
    SearchByLastEditedTime SearchSortDirection
  | -- | @{"property":"relevance"}@
    SearchByRelevance
  deriving stock (Generic, Show)

instance ToJSON SearchSort where
  toJSON (SearchByLastEditedTime dir) = Aeson.object ["timestamp" .= ("last_edited_time" :: Text), "direction" .= dir]
  toJSON SearchByRelevance = Aeson.object ["property" .= ("relevance" :: Text)]

-- | Object types supported by the search filter.
-- In API version 2025-09-03, the search API filters by @page@ or @data_source@.
data SearchObjectType
  = SearchPage
  | SearchDataSource
  deriving stock (Eq, Show, Generic)

instance ToJSON SearchObjectType where
  toJSON SearchPage = Aeson.String "page"
  toJSON SearchDataSource = Aeson.String "data_source"

instance FromJSON SearchObjectType where
  parseJSON = Aeson.withText "SearchObjectType" $ \case
    "page" -> pure SearchPage
    "data_source" -> pure SearchDataSource
    other -> fail $ "Unknown search object type: " <> unpack other

-- | Search filter
data SearchFilter
  = -- | @{"property":"object","value":...,"in_trash"?:...}@
    SearchObjectFilter SearchObjectType (Maybe Bool)
  | -- | @{"in_trash":...}@
    SearchInTrashFilter Bool
  deriving stock (Generic, Show)

instance ToJSON SearchFilter where
  toJSON (SearchObjectFilter v mTrash) =
    Aeson.object $ ["property" .= ("object" :: Text), "value" .= v] <> maybe [] (\t -> ["in_trash" .= t]) mTrash
  toJSON (SearchInTrashFilter t) = Aeson.object ["in_trash" .= t]

-- | Create a filter to search only for pages
pageFilter :: SearchFilter
pageFilter = SearchObjectFilter SearchPage Nothing

-- | Create a filter to search only for data sources
dataSourceFilter :: SearchFilter
dataSourceFilter = SearchObjectFilter SearchDataSource Nothing

-- | A search result: the same union as a data source query result.
type SearchResult = PageOrDataSource

-- | Servant API
type API =
  "search"
    :> ReqBody '[JSON] SearchRequest
    :> Post '[JSON] (ListOf PageOrDataSource)