packages feed

slack-web-2.2.0.0: src/Web/Slack/Pager.hs

module Web.Slack.Pager (
  Response,
  LoadPage,
  loadingPage,
  fetchAllBy,
  module Web.Slack.Pager.Types,
) where

import Data.Kind (Type)
import Web.Slack.Common qualified as Common
import Web.Slack.Pager.Types
import Web.Slack.Prelude

type Response a = Either Common.SlackClientError a

-- | Represents an action which returns a paginated response from Slack.
--   Every time calling the action, it performs a request with a new cursor
--   to get the next page.
--   If there is no more response, the action returns an empty list.
type LoadPage m a = m (Response [a])

type LoadPage :: forall {k}. (Type -> k) -> Type -> k

-- | Utility function for 'LoadPage'. Perform the 'LoadPage' action to call
--   the function with the loaded page, until an empty page is loaded.
loadingPage :: (Monad m, Monoid n) => LoadPage m a -> (Response [a] -> m n) -> m n
loadingPage loadPage usePage = go mempty
  where
    go result = do
      epage <- loadPage
      case epage of
        Right page ->
          if null page
            then return result
            else (go $!) . (result <>) =<< usePage epage
        Left e -> (result <>) <$> usePage (Left e)

fetchAllBy ::
  ( MonadIO m
  , PagedRequest req
  , PagedResponse resp
  ) =>
  (req -> m (Response resp)) ->
  req ->
  m (LoadPage m (ResponseObject resp))
fetchAllBy sendRequest initialRequest = do
  cursorRef <- liftIO $ newIORef Nothing

  let requestFromCursor cursor = setCursor cursor initialRequest
      collectAndUpdateCursor resp = do
        let newCursor = responseMetadataNextCursor =<< getResponseMetadata resp
            -- emptyCursor is used for the marker to show that there are no more pages.
            cursorToSave = if isNothing newCursor then emptyCursor else newCursor
        writeIORef cursorRef cursorToSave
        return $ getResponseData resp

  return $ do
    cursor <- liftIO $ readIORef cursorRef
    if cursor == emptyCursor
      then return $ Right []
      else
        traverse (liftIO . collectAndUpdateCursor)
          =<< sendRequest (requestFromCursor cursor)
  where
    -- Used for the marker to show that there are no more pages.
    emptyCursor = Just $ Common.Cursor ""