Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 09e1708

Browse files
authored
Merge pull request haskell-github#421 from phadej/execute-with-req
Add executeRequestWithMgrAndRes
2 parents 2503b54 + bbe378b commit 09e1708

File tree

8 files changed

+102
-54
lines changed

8 files changed

+102
-54
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ This reduces symbol bloat in the library.
1414
[#409](https://github.com/phadej/github/pull/409)
1515
- Update `Repo`, `NewRepo` and `EditRepo` data types
1616
[#407](https://github.com/phadej/github/pull/407)
17+
- Add `executeRequestWithMgrAndRes`
18+
[#421](https://github.com/phadej/github/pull/421)
19+
- Add `limitsFromHttpResponse`
20+
[#421](https://github.com/phadej/github/pull/421)
21+
- Add label descriptions
22+
[#418](https://github.com/phadej/github/pull/418)
1723

1824
## Changes for 0.23
1925

github.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ library
165165
, deepseq >=1.3.0.2 && <1.5
166166
, mtl >=2.1.3.1 && <2.2 || >=2.2.1 && <2.3
167167
, text >=1.2.0.6 && <1.3
168-
, time >=1.4 && <1.10
168+
, time-compat >=1.9.2.2 && <1.10
169169
, transformers >=0.3.0.0 && <0.6
170170

171171
-- other packages

samples/Operational/Operational.hs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,26 @@ module Main (main) where
77
import Common
88
import Prelude ()
99

10-
import Control.Monad.Operational
11-
import Control.Monad.Trans.Except (ExceptT (..), runExceptT)
12-
import Network.HTTP.Client (Manager, newManager)
10+
import Control.Exception (throw)
11+
import Control.Monad.IO.Class (liftIO)
12+
import Control.Monad.Operational (Program, ProgramViewT (..), singleton, view)
13+
import Control.Monad.Trans.Except (ExceptT (..), runExceptT)
14+
import Network.HTTP.Client (Manager, newManager, responseBody)
1315

14-
import qualified GitHub as GH
16+
import qualified GitHub as GH
1517

1618
data R a where
1719
R :: FromJSON a => GH.Request 'GH.RA a -> R a
1820

1921
type GithubMonad a = Program R a
2022

21-
runMonad :: Manager -> GH.Auth -> GithubMonad a -> ExceptT GH.Error IO a
23+
runMonad :: GH.AuthMethod auth => Manager -> auth -> GithubMonad a -> ExceptT GH.Error IO a
2224
runMonad mgr auth m = case view m of
2325
Return a -> return a
2426
R req :>>= k -> do
25-
b <- ExceptT $ GH.executeRequestWithMgr mgr auth req
26-
runMonad mgr auth (k b)
27+
res <- ExceptT $ GH.executeRequestWithMgrAndRes mgr auth req
28+
liftIO $ print $ GH.limitsFromHttpResponse res
29+
runMonad mgr auth (k (responseBody res))
2730

2831
githubRequest :: FromJSON a => GH.Request 'GH.RA a -> GithubMonad a
2932
githubRequest = singleton . R
@@ -33,9 +36,18 @@ main = GH.withOpenSSL $ do
3336
manager <- newManager GH.tlsManagerSettings
3437
auth' <- getAuth
3538
case auth' of
36-
Nothing -> return ()
39+
Nothing -> do
40+
(owner, rl) <- runExceptT (runMonad manager () script) >>= either throw return
41+
print owner
42+
print rl
3743
Just auth -> do
38-
owner <- runExceptT $ runMonad manager auth $ do
39-
repo <- githubRequest $ GH.repositoryR "phadej" "github"
40-
githubRequest $ GH.ownerInfoForR (GH.simpleOwnerLogin . GH.repoOwner $ repo)
44+
(owner, rl) <- runExceptT (runMonad manager auth script) >>= either throw return
4145
print owner
46+
print rl
47+
48+
script :: Program R (GH.Owner, GH.Limits)
49+
script = do
50+
repo <- githubRequest $ GH.repositoryR "phadej" "github"
51+
owner <- githubRequest $ GH.ownerInfoForR (GH.simpleOwnerLogin . GH.repoOwner $ repo)
52+
rl <- githubRequest GH.rateLimitR
53+
return (owner, GH.rateLimitCore rl)

src/GitHub/Auth.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,18 @@ instance Binary Auth
3030
instance Hashable Auth
3131

3232
-- | A type class for different authentication methods
33+
--
34+
-- Note the '()' intance, which doee nothing, i.e. is unauthenticated.
3335
class AuthMethod a where
3436
-- | Custom API endpoint without trailing slash
3537
endpoint :: a -> Maybe Text
3638
-- | A function which sets authorisation on an HTTP request
3739
setAuthRequest :: a -> HTTP.Request -> HTTP.Request
3840

41+
instance AuthMethod () where
42+
endpoint _ = Nothing
43+
setAuthRequest _ = id
44+
3945
instance AuthMethod Auth where
4046
endpoint (BasicAuth _ _) = Nothing
4147
endpoint (OAuth _) = Nothing

src/GitHub/Data/Deployments.hs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import Control.Arrow (second)
2121
import Data.ByteString (ByteString)
2222
import Data.Maybe (catMaybes)
2323
import Data.Text (Text)
24-
import Data.Time.Clock (UTCTime)
2524
import Data.Vector (Vector)
2625

2726
import GitHub.Data.Definitions (SimpleUser)

src/GitHub/Data/RateLimit.hs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@ module GitHub.Data.RateLimit where
88
import GitHub.Internal.Prelude
99
import Prelude ()
1010

11+
import Data.Time.Clock.System.Compat (SystemTime (..))
12+
13+
import qualified Data.ByteString.Char8 as BS8
14+
import qualified Network.HTTP.Client as HTTP
15+
1116
data Limits = Limits
1217
{ limitsMax :: !Int
1318
, limitsRemaining :: !Int
14-
, limitsReset :: !Int -- TODO: change to proper type
19+
, limitsReset :: !SystemTime
1520
}
16-
deriving (Show, Data, Typeable, Eq, Ord, Generic)
21+
deriving (Show, {- Data, -} Typeable, Eq, Ord, Generic)
1722

1823
instance NFData Limits where rnf = genericRnf
1924
instance Binary Limits
@@ -22,14 +27,14 @@ instance FromJSON Limits where
2227
parseJSON = withObject "Limits" $ \obj -> Limits
2328
<$> obj .: "limit"
2429
<*> obj .: "remaining"
25-
<*> obj .: "reset"
30+
<*> fmap (\t -> MkSystemTime t 0) (obj .: "reset")
2631

2732
data RateLimit = RateLimit
2833
{ rateLimitCore :: Limits
2934
, rateLimitSearch :: Limits
3035
, rateLimitGraphQL :: Limits
3136
}
32-
deriving (Show, Data, Typeable, Eq, Ord, Generic)
37+
deriving (Show, {- Data, -} Typeable, Eq, Ord, Generic)
3338

3439
instance NFData RateLimit where rnf = genericRnf
3540
instance Binary RateLimit
@@ -41,3 +46,21 @@ instance FromJSON RateLimit where
4146
<$> resources .: "core"
4247
<*> resources .: "search"
4348
<*> resources .: "graphql"
49+
50+
-------------------------------------------------------------------------------
51+
-- Extras
52+
-------------------------------------------------------------------------------
53+
54+
-- | @since 0.24
55+
limitsFromHttpResponse :: HTTP.Response a -> Maybe Limits
56+
limitsFromHttpResponse res = do
57+
let hdrs = HTTP.responseHeaders res
58+
m <- lookup "X-RateLimit-Limit" hdrs >>= readIntegral
59+
r <- lookup "X-RateLimit-Remaining" hdrs >>= readIntegral
60+
t <- lookup "X-RateLimit-Reset" hdrs >>= readIntegral
61+
return (Limits m r (MkSystemTime t 0))
62+
where
63+
readIntegral :: Num a => BS8.ByteString -> Maybe a
64+
readIntegral bs = case BS8.readInt bs of
65+
Just (n, bs') | BS8.null bs' -> Just (fromIntegral n)
66+
_ -> Nothing

src/GitHub/Internal/Prelude.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ import Data.Maybe (catMaybes)
5353
import Data.Semigroup (Semigroup (..))
5454
import Data.String (IsString (..))
5555
import Data.Text (Text, pack, unpack)
56-
import Data.Time (UTCTime)
56+
import Data.Time.Compat (UTCTime)
5757
import Data.Time.ISO8601 (formatISO8601)
5858
import Data.Vector (Vector)
5959
import Data.Vector.Instances ()

src/GitHub/Request.hs

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ module GitHub.Request (
4646
-- * Request execution in IO
4747
executeRequest,
4848
executeRequestWithMgr,
49+
executeRequestWithMgrAndRes,
4950
executeRequest',
5051
executeRequestWithMgr',
5152
executeRequestMaybe,
@@ -66,7 +67,7 @@ module GitHub.Request (
6667
-- | This always exist, independently of @openssl@ configuration flag.
6768
-- They change accordingly, to make use of the library simpler.
6869
withOpenSSL,
69-
tlsManagerSettings,
70+
tlsManagerSettings,
7071
) where
7172

7273
import GitHub.Internal.Prelude
@@ -112,7 +113,7 @@ import qualified OpenSSL.Session as SSL
112113
import qualified OpenSSL.X509.SystemStore as SSL
113114
#endif
114115

115-
import GitHub.Auth (Auth, AuthMethod, endpoint, setAuthRequest)
116+
import GitHub.Auth (AuthMethod, endpoint, setAuthRequest)
116117
import GitHub.Data (Error (..))
117118
import GitHub.Data.PullRequests (MergeResult (..))
118119
import GitHub.Data.Request
@@ -206,33 +207,46 @@ lessFetchCount :: Int -> FetchCount -> Bool
206207
lessFetchCount _ FetchAll = True
207208
lessFetchCount i (FetchAtLeast j) = i < fromIntegral j
208209

210+
209211
-- | Like 'executeRequest' but with provided 'Manager'.
210212
executeRequestWithMgr
211213
:: (AuthMethod am, ParseResponse mt a)
212214
=> Manager
213215
-> am
214216
-> GenRequest mt rw a
215217
-> IO (Either Error a)
216-
executeRequestWithMgr mgr auth req = runExceptT $ do
218+
executeRequestWithMgr mgr auth req =
219+
fmap (fmap responseBody) (executeRequestWithMgrAndRes mgr auth req)
220+
221+
-- | Execute request and return the last received 'HTTP.Response'.
222+
--
223+
-- @since 0.24
224+
executeRequestWithMgrAndRes
225+
:: (AuthMethod am, ParseResponse mt a)
226+
=> Manager
227+
-> am
228+
-> GenRequest mt rw a
229+
-> IO (Either Error (HTTP.Response a))
230+
executeRequestWithMgrAndRes mgr auth req = runExceptT $ do
217231
httpReq <- makeHttpRequest (Just auth) req
218232
performHttpReq httpReq req
219233
where
220-
httpLbs' :: HTTP.Request -> ExceptT Error IO (Response LBS.ByteString)
234+
httpLbs' :: HTTP.Request -> ExceptT Error IO (HTTP.Response LBS.ByteString)
221235
httpLbs' req' = lift (httpLbs req' mgr) `catch` onHttpException
222236

223-
performHttpReq :: forall rw mt b. ParseResponse mt b => HTTP.Request -> GenRequest mt rw b -> ExceptT Error IO b
237+
performHttpReq :: forall rw mt b. ParseResponse mt b => HTTP.Request -> GenRequest mt rw b -> ExceptT Error IO (HTTP.Response b)
224238
performHttpReq httpReq Query {} = do
225239
res <- httpLbs' httpReq
226-
unTagged (parseResponse httpReq res :: Tagged mt (ExceptT Error IO b))
240+
(<$ res) <$> unTagged (parseResponse httpReq res :: Tagged mt (ExceptT Error IO b))
227241

228242
performHttpReq httpReq (PagedQuery _ _ l) =
229-
unTagged (performPagedRequest httpLbs' predicate httpReq :: Tagged mt (ExceptT Error IO b))
243+
unTagged (performPagedRequest httpLbs' predicate httpReq :: Tagged mt (ExceptT Error IO (HTTP.Response b)))
230244
where
231245
predicate v = lessFetchCount (V.length v) l
232246

233247
performHttpReq httpReq (Command _ _ _) = do
234248
res <- httpLbs' httpReq
235-
unTagged (parseResponse httpReq res :: Tagged mt (ExceptT Error IO b))
249+
(<$ res) <$> unTagged (parseResponse httpReq res :: Tagged mt (ExceptT Error IO b))
236250

237251
-- | Like 'executeRequest' but without authentication.
238252
executeRequest' :: ParseResponse mt a => GenRequest mt 'RO a -> IO (Either Error a)
@@ -246,21 +260,7 @@ executeRequestWithMgr'
246260
=> Manager
247261
-> GenRequest mt 'RO a
248262
-> IO (Either Error a)
249-
executeRequestWithMgr' mgr req = runExceptT $ do
250-
httpReq <- makeHttpRequest (Nothing :: Maybe Auth) req
251-
performHttpReq httpReq req
252-
where
253-
httpLbs' :: HTTP.Request -> ExceptT Error IO (Response LBS.ByteString)
254-
httpLbs' req' = lift (httpLbs req' mgr) `catch` onHttpException
255-
256-
performHttpReq :: forall mt b. ParseResponse mt b => HTTP.Request -> GenRequest mt 'RO b -> ExceptT Error IO b
257-
performHttpReq httpReq Query {} = do
258-
res <- httpLbs' httpReq
259-
unTagged (parseResponse httpReq res :: Tagged mt (ExceptT Error IO b))
260-
performHttpReq httpReq (PagedQuery _ _ l) =
261-
unTagged (performPagedRequest httpLbs' predicate httpReq :: Tagged mt (ExceptT Error IO b))
262-
where
263-
predicate v = lessFetchCount (V.length v) l
263+
executeRequestWithMgr' mgr = executeRequestWithMgr mgr ()
264264

265265
-- | Helper for picking between 'executeRequest' and 'executeRequest''.
266266
--
@@ -302,9 +302,9 @@ class Accept mt => ParseResponse (mt :: MediaType *) a where
302302
-- | Parse API response.
303303
--
304304
-- @
305-
-- parseResponse :: 'FromJSON' a => 'Response' 'LBS.ByteString' -> 'Either' 'Error' a
305+
-- parseResponse :: 'FromJSON' a => 'HTTP.Response' 'LBS.ByteString' -> 'Either' 'Error' a
306306
-- @
307-
parseResponseJSON :: (FromJSON a, MonadError Error m) => Response LBS.ByteString -> m a
307+
parseResponseJSON :: (FromJSON a, MonadError Error m) => HTTP.Response LBS.ByteString -> m a
308308
parseResponseJSON res = case eitherDecode (responseBody res) of
309309
Right x -> return x
310310
Left err -> throwError . ParseError . T.pack $ err
@@ -349,9 +349,9 @@ instance b ~ URI => ParseResponse 'MtRedirect b where
349349
-- | Helper for handling of 'RequestRedirect'.
350350
--
351351
-- @
352-
-- parseRedirect :: 'Response' 'LBS.ByteString' -> 'Either' 'Error' a
352+
-- parseRedirect :: 'HTTP.Response' 'LBS.ByteString' -> 'Either' 'Error' a
353353
-- @
354-
parseRedirect :: MonadError Error m => URI -> Response LBS.ByteString -> m URI
354+
parseRedirect :: MonadError Error m => URI -> HTTP.Response LBS.ByteString -> m URI
355355
parseRedirect originalUri rsp = do
356356
let status = responseStatus rsp
357357
when (statusCode status /= 302) $
@@ -501,7 +501,7 @@ makeHttpRequest auth r = case r of
501501
setBody body req = req { requestBody = RequestBodyLBS body }
502502

503503
-- | Query @Link@ header with @rel=next@ from the request headers.
504-
getNextUrl :: Response a -> Maybe URI
504+
getNextUrl :: HTTP.Response a -> Maybe URI
505505
getNextUrl req = do
506506
linkHeader <- lookup "Link" (responseHeaders req)
507507
links <- parseLinkHeaderBS linkHeader
@@ -516,33 +516,35 @@ getNextUrl req = do
516516

517517
-- | Helper for making paginated requests. Responses, @a@ are combined monoidally.
518518
--
519+
-- The result is wrapped in the last received 'HTTP.Response'.
520+
--
519521
-- @
520522
-- performPagedRequest :: ('FromJSON' a, 'Semigroup' a)
521-
-- => ('HTTP.Request' -> 'ExceptT' 'Error' 'IO' ('Response' 'LBS.ByteString'))
523+
-- => ('HTTP.Request' -> 'ExceptT' 'Error' 'IO' ('HTTP.Response' 'LBS.ByteString'))
522524
-- -> (a -> 'Bool')
523525
-- -> 'HTTP.Request'
524-
-- -> 'ExceptT' 'Error' 'IO' a
526+
-- -> 'ExceptT' 'Error' 'IO' ('HTTP.Response' a)
525527
-- @
526528
performPagedRequest
527529
:: forall a m mt. (ParseResponse mt a, Semigroup a, MonadCatch m, MonadError Error m)
528-
=> (HTTP.Request -> m (Response LBS.ByteString)) -- ^ `httpLbs` analogue
529-
-> (a -> Bool) -- ^ predicate to continue iteration
530-
-> HTTP.Request -- ^ initial request
531-
-> Tagged mt (m a)
530+
=> (HTTP.Request -> m (HTTP.Response LBS.ByteString)) -- ^ `httpLbs` analogue
531+
-> (a -> Bool) -- ^ predicate to continue iteration
532+
-> HTTP.Request -- ^ initial request
533+
-> Tagged mt (m (HTTP.Response a))
532534
performPagedRequest httpLbs' predicate initReq = Tagged $ do
533535
res <- httpLbs' initReq
534536
m <- unTagged (parseResponse initReq res :: Tagged mt (m a))
535537
go m res initReq
536538
where
537-
go :: a -> Response LBS.ByteString -> HTTP.Request -> m a
539+
go :: a -> HTTP.Response LBS.ByteString -> HTTP.Request -> m (HTTP.Response a)
538540
go acc res req =
539541
case (predicate acc, getNextUrl res) of
540542
(True, Just uri) -> do
541543
req' <- HTTP.setUri req uri
542544
res' <- httpLbs' req'
543545
m <- unTagged (parseResponse req' res' :: Tagged mt (m a))
544546
go (acc <> m) res' req'
545-
(_, _) -> return acc
547+
(_, _) -> return (acc <$ res)
546548

547549
-------------------------------------------------------------------------------
548550
-- Internal

0 commit comments

Comments
 (0)