notion-client-0.1.0.0: src/Notion/V1/Search.hs
-- | @\/v1\/search@
module Notion.V1.Search
( -- * Main types
SearchRequest (..),
_SearchRequest,
SearchSortDirection (..),
SearchSort (..),
SearchFilter (..),
SearchObjectType (..),
-- * Response parsing
SearchResult (..),
parseSearchResults,
-- * Convenience constructors
pageFilter,
dataSourceFilter,
-- * Servant
API,
)
where
import Data.Aeson qualified as Aeson
import Data.Vector qualified as Vector
import Notion.Prelude
import Notion.V1.DataSources (DataSourceObject)
import Notion.V1.ListOf (ListOf (..))
import Notion.V1.Pages (PageObject)
-- | 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 = SearchSort
{ direction :: SearchSortDirection,
timestamp :: Text
}
deriving stock (Generic, Show)
instance ToJSON SearchSort where
toJSON = genericToJSON aesonOptions
-- | 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 = SearchFilter
{ value :: SearchObjectType,
property :: Text
}
deriving stock (Generic, Show)
instance ToJSON SearchFilter where
toJSON = genericToJSON aesonOptions
-- | Create a filter to search only for pages
pageFilter :: SearchFilter
pageFilter = SearchFilter {value = SearchPage, property = "object"}
-- | Create a filter to search only for data sources
dataSourceFilter :: SearchFilter
dataSourceFilter = SearchFilter {value = SearchDataSource, property = "object"}
-- | Servant API
type API =
"search"
:> ReqBody '[JSON] SearchRequest
:> Post '[JSON] (ListOf Aeson.Value)
-- * Response parsing
-- | A search result can be either a page or a data source
data SearchResult
= PageResult PageObject
| DataSourceResult DataSourceObject
deriving stock (Show)
instance FromJSON SearchResult where
parseJSON v = do
obj <- Aeson.parseJSON v
objectType <- obj Aeson..: "object"
case objectType of
"page" -> PageResult <$> Aeson.parseJSON v
"data_source" -> DataSourceResult <$> Aeson.parseJSON v
other -> fail $ "Unknown object type in search result: " <> other
-- | Parse raw search results into typed 'SearchResult' values.
-- Results that fail to parse are silently dropped.
parseSearchResults :: ListOf Aeson.Value -> Vector SearchResult
parseSearchResults listOf =
Vector.mapMaybe parseOne (results listOf)
where
parseOne :: Aeson.Value -> Maybe SearchResult
parseOne v = case Aeson.fromJSON v of
Aeson.Success r -> Just r
Aeson.Error _ -> Nothing