-
Notifications
You must be signed in to change notification settings - Fork 59
/
Copy pathRateLimit.hs
84 lines (75 loc) · 2.91 KB
/
RateLimit.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -Wno-overlapping-patterns #-}
module Lentille.GitHub.RateLimit where
import Data.Morpheus.Client
import Lentille
import Lentille.GitHub.Types
import Lentille.GraphQL
import Monocle.Prelude
import Network.HTTP.Client (responseBody, responseStatus)
import Network.HTTP.Types (Status, badGateway502, forbidden403, ok200, unauthorized401)
import Effectful.Retry
declareLocalTypesInline
ghSchemaLocation
[raw|
query GetRateLimit {
rateLimit {
used
remaining
resetAt
}
}
|]
transformResponse :: GetRateLimit -> Maybe RateLimit
transformResponse = \case
GetRateLimit
( Just
(GetRateLimitRateLimit used remaining (DateTime resetAt'))
) -> case parseDateValue $ from resetAt' of
Just resetAt -> Just RateLimit {..}
Nothing -> error $ "Unable to parse the resetAt date string: " <> resetAt'
GetRateLimit Nothing -> Nothing
respOther -> error ("Invalid response: " <> show respOther)
getRateLimit :: GraphEffects es => GraphClient -> Eff es (Either GraphQLError (Maybe RateLimit))
getRateLimit client = do
fmap transformResponse
<$> doRequest client mkRateLimitArgs retryCheck Nothing Nothing
where
mkRateLimitArgs = const . const $ ()
retryCheck :: forall es a. GraphEffects es => Either GraphQLError a -> Eff es RetryAction
retryCheck = \case
Right _ -> pure DontRetry
Left (GraphQLError err (RequestLog _ _ resp _))
| status == unauthorized401 -> do
logWarn "Authentication error" ["body" .= body]
pure DontRetry
| isTimeoutError status body -> do
logWarn_ "Server side timeout error. Will retry with lower query depth ..."
pure ConsultPolicy
| isSecondaryRateLimitError status body -> do
logWarn_ "Secondary rate limit error. Will retry after 60 seconds ..."
pure (ConsultPolicyOverrideDelay $ 60 * 1_000_000)
| isRepoNotFound status body -> do
logWarn_ "Repository not found. Will not retry."
pure DontRetry
| otherwise -> do
logWarn "Unexpected error" ["err" .= show @Text err]
pure ConsultPolicy
where
status = responseStatus resp
body = decodeUtf8 $ responseBody resp
where
isTimeoutError :: Status -> Text -> Bool
isTimeoutError status body =
let msg = "Something went wrong while executing your query. This may be the result of a timeout"
in status == badGateway502 && inText msg body
-- https://docs.github.com/en/rest/overview/resources-in-the-rest-api#secondary-rate-limits
isSecondaryRateLimitError :: Status -> Text -> Bool
isSecondaryRateLimitError status body =
let msg = "You have exceeded a secondary rate limit."
in status == forbidden403 && inText msg body
isRepoNotFound :: Status -> Text -> Bool
isRepoNotFound status body =
let msg = "Could not resolve to a Repository with the name"
in status == ok200 && inText msg body