{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE TupleSections #-} module Network.DigitalOcean.Utils.Pagination where ----------------------------------------------------------------- import Data.Aeson import Data.Aeson.Types import qualified Data.Text as T import qualified Data.Text as T ----------------------------------------------------------------- import Network.DigitalOcean.Types import qualified Data.Set as Set ----------------------------------------------------------------- paginate :: (Paginatable a, FromJSON (PaginationState a)) => (String -> DO (PaginationState a)) -> PaginationState a -> DO (PaginationState a) paginate f s = case nextUrl s of Just url -> do newState <- f url return $ newState { curr = curr s ++ curr newState } Nothing -> return s { isLast = True } paginationQueryParams :: Maybe PaginationConfig -> QueryParams paginationQueryParams Nothing = [] paginationQueryParams (Just (PaginationConfig pageSize _)) = [("per_page", show pageSize)] paginateUntil :: (Paginatable a, FromJSON (PaginationState a)) => PaginationConfig -> PaginationState a -> (String -> DO (PaginationState a)) -> DO (PaginationState a) paginateUntil config@PaginationConfig {..} state@PaginationState {..} f = if length curr >= resultLimit || isLast then return state else do newState <- paginate f state paginateUntil config newState f -- Taken from: https://stackoverflow.com/questions/28368980/composing-optional-aeson-parsers (/?) :: FromJSON a => Parser (Maybe Object) -> T.Text -> Parser (Maybe a) maybeParser /? key = maybeParser >>= maybe (return Nothing) (.:? key) parsePaginationState :: Paginatable a => Object -> T.Text -> Parser (PaginationState a) parsePaginationState v key = do values <- v .: key (next, total) <- parse_meta let page = 1 return $ PaginationState values page next total False where parse_meta :: Parser (Maybe String, Maybe Int) parse_meta = (,) <$> v .:? "links" /? "pages" /? "next" <*> (v .:? "meta" /? "total")