@@ -54,6 +54,7 @@ module GitHub.Request (
5454 ParseResponse (.. ),
5555 makeHttpRequest ,
5656 parseStatus ,
57+ parsePageLinks ,
5758 StatusMap ,
5859 getNextUrl ,
5960 performPagedRequest ,
@@ -79,6 +80,7 @@ import Control.Monad.Trans.Class (lift)
7980import Control.Monad.Trans.Except (ExceptT (.. ), runExceptT )
8081import Data.Aeson (eitherDecode )
8182import Data.List (find )
83+ import Data.Maybe (fromMaybe )
8284import Data.Tagged (Tagged (.. ))
8385import Data.Version (showVersion )
8486
@@ -87,13 +89,14 @@ import Network.HTTP.Client
8789 httpLbs , method , newManager , redirectCount , requestBody , requestHeaders ,
8890 setQueryString , setRequestIgnoreStatus )
8991import Network.HTTP.Link.Parser (parseLinkHeaderBS )
90- import Network.HTTP.Link.Types (LinkParam (.. ), href , linkParams )
92+ import Network.HTTP.Link.Types (Link ( .. ), LinkParam (.. ), href , linkParams )
9193import Network.HTTP.Types (Method , RequestHeaders , Status (.. ))
9294import Network.URI
9395 (URI , escapeURIString , isUnescapedInURIComponent , parseURIReference ,
9496 relativeTo )
9597
9698import qualified Data.ByteString as BS
99+ import Data.ByteString.Builder (intDec , toLazyByteString )
97100import qualified Data.ByteString.Lazy as LBS
98101import qualified Data.Text as T
99102import qualified Data.Text.Encoding as TE
@@ -199,11 +202,6 @@ executeRequest auth req = withOpenSSL $ do
199202 manager <- newManager tlsManagerSettings
200203 executeRequestWithMgr manager auth req
201204
202- lessFetchCount :: Int -> FetchCount -> Bool
203- lessFetchCount _ FetchAll = True
204- lessFetchCount i (FetchAtLeast j) = i < fromIntegral j
205-
206-
207205-- | Like 'executeRequest' but with provided 'Manager'.
208206executeRequestWithMgr
209207 :: (AuthMethod am , ParseResponse mt a )
@@ -235,10 +233,13 @@ executeRequestWithMgrAndRes mgr auth req = runExceptT $ do
235233 res <- httpLbs' httpReq
236234 (<$ res) <$> unTagged (parseResponse httpReq res :: Tagged mt (ExceptT Error IO b ))
237235
238- performHttpReq httpReq (PagedQuery _ _ l) =
239- unTagged (performPagedRequest httpLbs' predicate httpReq :: Tagged mt (ExceptT Error IO (HTTP. Response b )))
240- where
241- predicate v = lessFetchCount (length v) l
236+ performHttpReq httpReq (PagedQuery _ _ (FetchPage _)) = do
237+ (res, _pageLinks) <- unTagged (performPerPageRequest httpLbs' httpReq :: Tagged mt (ExceptT Error IO (HTTP. Response b , PageLinks )))
238+ return res
239+ performHttpReq httpReq (PagedQuery _ _ FetchAll ) =
240+ unTagged (performPagedRequest httpLbs' (const True ) httpReq :: Tagged mt (ExceptT Error IO (HTTP. Response b )))
241+ performHttpReq httpReq (PagedQuery _ _ (FetchAtLeast j)) =
242+ unTagged (performPagedRequest httpLbs' (\ v -> length v < fromIntegral j) httpReq :: Tagged mt (ExceptT Error IO (HTTP. Response b )))
242243
243244 performHttpReq httpReq (Command _ _ _) = do
244245 res <- httpLbs' httpReq
@@ -456,15 +457,15 @@ makeHttpRequest auth r = case r of
456457 $ setReqHeaders
457458 . unTagged (modifyRequest :: Tagged mt (HTTP. Request -> HTTP. Request ))
458459 . maybe id setAuthRequest auth
459- . setQueryString qs
460+ . setQueryString (qs <> extraQueryItems)
460461 $ req
461462 PagedQuery paths qs _ -> do
462463 req <- parseUrl' $ url paths
463464 return
464465 $ setReqHeaders
465466 . unTagged (modifyRequest :: Tagged mt (HTTP. Request -> HTTP. Request ))
466467 . maybe id setAuthRequest auth
467- . setQueryString qs
468+ . setQueryString (qs <> extraQueryItems)
468469 $ req
469470 Command m paths body -> do
470471 req <- parseUrl' $ url paths
@@ -496,6 +497,14 @@ makeHttpRequest auth r = case r of
496497 setBody :: LBS. ByteString -> HTTP. Request -> HTTP. Request
497498 setBody body req = req { requestBody = RequestBodyLBS body }
498499
500+ extraQueryItems :: [(BS. ByteString , Maybe BS. ByteString )]
501+ extraQueryItems = case r of
502+ PagedQuery _ _ (FetchPage pp) -> catMaybes [
503+ (\ page -> (" page" , Just (BS. toStrict $ toLazyByteString $ intDec page))) <$> pageParamsPage pp
504+ , (\ perPage -> (" per_page" , Just (BS. toStrict $ toLazyByteString $ intDec perPage))) <$> pageParamsPerPage pp
505+ ]
506+ _ -> []
507+
499508-- | Query @Link@ header with @rel=next@ from the request headers.
500509getNextUrl :: HTTP. Response a -> Maybe URI
501510getNextUrl req = do
@@ -542,6 +551,35 @@ performPagedRequest httpLbs' predicate initReq = Tagged $ do
542551 go (acc <> m) res' req'
543552 (_, _) -> return (acc <$ res)
544553
554+ -- | Helper for requesting a single page, as specified by 'PageParams'.
555+ --
556+ -- This parses and returns the 'PageLinks' alongside the HTTP response.
557+ performPerPageRequest
558+ :: forall a m mt . (ParseResponse mt a , MonadCatch m , MonadError Error m )
559+ => (HTTP. Request -> m (HTTP. Response LBS. ByteString )) -- ^ `httpLbs` analogue
560+ -> HTTP. Request -- ^ initial request
561+ -> Tagged mt (m (HTTP. Response a , PageLinks ))
562+ performPerPageRequest httpLbs' initReq = Tagged $ do
563+ res <- httpLbs' initReq
564+ m <- unTagged (parseResponse initReq res :: Tagged mt (m a ))
565+ return (m <$ res, parsePageLinks res)
566+
567+ -- | Parse the 'PageLinks' from an HTTP response, where the information is
568+ -- encoded in the Link header.
569+ parsePageLinks :: HTTP. Response a -> PageLinks
570+ parsePageLinks res = PageLinks {
571+ pageLinksPrev = linkToUri <$> find (elem (Rel , " prev" ) . linkParams) links
572+ , pageLinksNext = linkToUri <$> find (elem (Rel , " next" ) . linkParams) links
573+ , pageLinksLast = linkToUri <$> find (elem (Rel , " last" ) . linkParams) links
574+ , pageLinksFirst = linkToUri <$> find (elem (Rel , " first" ) . linkParams) links
575+ }
576+ where
577+ links :: [Link URI ]
578+ links = fromMaybe [] (lookup " Link" (responseHeaders res) >>= parseLinkHeaderBS)
579+
580+ linkToUri :: Link URI -> URI
581+ linkToUri (Link uri _) = uri
582+
545583-------------------------------------------------------------------------------
546584-- Internal
547585-------------------------------------------------------------------------------
0 commit comments