module Asana.Api.Request
  ( AsanaAccessKey(..)
  , HasAsanaAccessKey(..)
  , Single(..)
  , Page(..)
  , NextPage(..)
  , ApiData(..)
  , getAll
  , getAllParams
  , getSingle
  , put
  , post
  , maxRequests
  ) where

import Asana.Api.Prelude

import Data.Aeson
import Data.Aeson.Casing (aesonPrefix, snakeCase)
import qualified Data.ByteString.Lazy as BSL
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.Encoding.Error as T
import Network.HTTP.Simple
  ( JSONException(JSONConversionException, JSONParseException)
  , Request
  , Response
  , addRequestHeader
  , getResponseBody
  , getResponseHeader
  , getResponseStatusCode
  , httpJSON
  , parseRequest_
  , setRequestBodyJSON
  , setRequestMethod
  )
import UnliftIO.Concurrent (threadDelay)

newtype AsanaAccessKey = AsanaAccessKey
    { AsanaAccessKey -> Text
unAsanaAccessKey :: Text
    }

class HasAsanaAccessKey env where
  asanaAccessKeyL :: Lens' env AsanaAccessKey

instance HasAsanaAccessKey AsanaAccessKey where
  asanaAccessKeyL :: Lens' AsanaAccessKey AsanaAccessKey
asanaAccessKeyL = forall a. a -> a
id

maxRequests :: Int
maxRequests :: Int
maxRequests = Int
50

-- | Type for a single-resource response, containing @{ data: { ... } }@
newtype Single a = Single
  { forall a. Single a -> a
sData :: a
  }
  deriving newtype (Single a -> Single a -> Bool
forall a. Eq a => Single a -> Single a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Single a -> Single a -> Bool
$c/= :: forall a. Eq a => Single a -> Single a -> Bool
== :: Single a -> Single a -> Bool
$c== :: forall a. Eq a => Single a -> Single a -> Bool
Eq, Int -> Single a -> ShowS
[Single a] -> ShowS
Single a -> String
forall a. Show a => Int -> Single a -> ShowS
forall a. Show a => [Single a] -> ShowS
forall a. Show a => Single a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Single a] -> ShowS
$cshowList :: forall a. Show a => [Single a] -> ShowS
show :: Single a -> String
$cshow :: forall a. Show a => Single a -> String
showsPrec :: Int -> Single a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Single a -> ShowS
Show)
  deriving stock forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall a x. Rep (Single a) x -> Single a
forall a x. Single a -> Rep (Single a) x
$cto :: forall a x. Rep (Single a) x -> Single a
$cfrom :: forall a x. Single a -> Rep (Single a) x
Generic

instance FromJSON a => FromJSON (Single a) where
  parseJSON :: Value -> Parser (Single a)
parseJSON = forall a.
(Generic a, GFromJSON Zero (Rep a)) =>
Options -> Value -> Parser a
genericParseJSON forall a b. (a -> b) -> a -> b
$ ShowS -> Options
aesonPrefix ShowS
snakeCase

-- | Type for a list-resource response, containing @{ data: [{ ... }] }@
data Page a = Page
  { forall a. Page a -> [a]
pData :: [a]
  , forall a. Page a -> Maybe NextPage
pNextPage :: Maybe NextPage
  }
  deriving stock (Page a -> Page a -> Bool
forall a. Eq a => Page a -> Page a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Page a -> Page a -> Bool
$c/= :: forall a. Eq a => Page a -> Page a -> Bool
== :: Page a -> Page a -> Bool
$c== :: forall a. Eq a => Page a -> Page a -> Bool
Eq, forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall a x. Rep (Page a) x -> Page a
forall a x. Page a -> Rep (Page a) x
$cto :: forall a x. Rep (Page a) x -> Page a
$cfrom :: forall a x. Page a -> Rep (Page a) x
Generic, Int -> Page a -> ShowS
forall a. Show a => Int -> Page a -> ShowS
forall a. Show a => [Page a] -> ShowS
forall a. Show a => Page a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Page a] -> ShowS
$cshowList :: forall a. Show a => [Page a] -> ShowS
show :: Page a -> String
$cshow :: forall a. Show a => Page a -> String
showsPrec :: Int -> Page a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Page a -> ShowS
Show)

instance FromJSON a => FromJSON (Page a) where
  parseJSON :: Value -> Parser (Page a)
parseJSON = forall a.
(Generic a, GFromJSON Zero (Rep a)) =>
Options -> Value -> Parser a
genericParseJSON forall a b. (a -> b) -> a -> b
$ ShowS -> Options
aesonPrefix ShowS
snakeCase

-- | The @next_page@ element of a paginated response
data NextPage = NextPage
  { NextPage -> Text
npOffset :: Text
  , NextPage -> Text
npPath :: Text
  , NextPage -> Text
npUri :: Text
  }
  deriving stock (NextPage -> NextPage -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: NextPage -> NextPage -> Bool
$c/= :: NextPage -> NextPage -> Bool
== :: NextPage -> NextPage -> Bool
$c== :: NextPage -> NextPage -> Bool
Eq, forall x. Rep NextPage x -> NextPage
forall x. NextPage -> Rep NextPage x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep NextPage x -> NextPage
$cfrom :: forall x. NextPage -> Rep NextPage x
Generic, Int -> NextPage -> ShowS
[NextPage] -> ShowS
NextPage -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [NextPage] -> ShowS
$cshowList :: [NextPage] -> ShowS
show :: NextPage -> String
$cshow :: NextPage -> String
showsPrec :: Int -> NextPage -> ShowS
$cshowsPrec :: Int -> NextPage -> ShowS
Show)

instance FromJSON NextPage where
  parseJSON :: Value -> Parser NextPage
parseJSON = forall a.
(Generic a, GFromJSON Zero (Rep a)) =>
Options -> Value -> Parser a
genericParseJSON forall a b. (a -> b) -> a -> b
$ ShowS -> Options
aesonPrefix ShowS
snakeCase

-- | Generic type for un/wrapping an item as @{ data: <item> }@
newtype ApiData a = ApiData
  { forall a. ApiData a -> a
adData :: a
  }
  deriving newtype (Int -> ApiData a -> ShowS
[ApiData a] -> ShowS
ApiData a -> String
forall a. Show a => Int -> ApiData a -> ShowS
forall a. Show a => [ApiData a] -> ShowS
forall a. Show a => ApiData a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ApiData a] -> ShowS
$cshowList :: forall a. Show a => [ApiData a] -> ShowS
show :: ApiData a -> String
$cshow :: forall a. Show a => ApiData a -> String
showsPrec :: Int -> ApiData a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> ApiData a -> ShowS
Show, ApiData a -> ApiData a -> Bool
forall a. Eq a => ApiData a -> ApiData a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ApiData a -> ApiData a -> Bool
$c/= :: forall a. Eq a => ApiData a -> ApiData a -> Bool
== :: ApiData a -> ApiData a -> Bool
$c== :: forall a. Eq a => ApiData a -> ApiData a -> Bool
Eq)
  deriving stock forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall a x. Rep (ApiData a) x -> ApiData a
forall a x. ApiData a -> Rep (ApiData a) x
$cto :: forall a x. Rep (ApiData a) x -> ApiData a
$cfrom :: forall a x. ApiData a -> Rep (ApiData a) x
Generic

instance FromJSON a => FromJSON (ApiData a) where
  parseJSON :: Value -> Parser (ApiData a)
parseJSON = forall a.
(Generic a, GFromJSON Zero (Rep a)) =>
Options -> Value -> Parser a
genericParseJSON forall a b. (a -> b) -> a -> b
$ ShowS -> Options
aesonPrefix ShowS
snakeCase

instance ToJSON a => ToJSON (ApiData a) where
  toJSON :: ApiData a -> Value
toJSON = forall a.
(Generic a, GToJSON' Value Zero (Rep a)) =>
Options -> a -> Value
genericToJSON forall a b. (a -> b) -> a -> b
$ ShowS -> Options
aesonPrefix ShowS
snakeCase
  toEncoding :: ApiData a -> Encoding
toEncoding = forall a.
(Generic a, GToJSON' Encoding Zero (Rep a)) =>
Options -> a -> Encoding
genericToEncoding forall a b. (a -> b) -> a -> b
$ ShowS -> Options
aesonPrefix ShowS
snakeCase

-- | Naively GET all pages of a paginated resource
getAll
  :: ( MonadUnliftIO m
     , MonadLogger m
     , MonadReader env m
     , HasAsanaAccessKey env
     , FromJSON a
     )
  => String
  -> m [a]
getAll :: forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, FromJSON a) =>
String -> m [a]
getAll String
path = forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, FromJSON a) =>
String -> [(String, String)] -> m [a]
getAllParams String
path []

getAllParams
  :: ( MonadUnliftIO m
     , MonadLogger m
     , MonadReader env m
     , HasAsanaAccessKey env
     , FromJSON a
     )
  => String
  -> [(String, String)]
  -> m [a]
getAllParams :: forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, FromJSON a) =>
String -> [(String, String)] -> m [a]
getAllParams String
path [(String, String)]
params = Maybe String -> m [a]
go forall a. Maybe a
Nothing
 where
  go :: Maybe String -> m [a]
go Maybe String
mOffset = do
    Page [a]
d Maybe NextPage
mNextPage <- forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, FromJSON a) =>
String -> [(String, String)] -> Int -> Maybe String -> m a
get String
path [(String, String)]
params Int
50 Maybe String
mOffset

    forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall (f :: * -> *) a. Applicative f => a -> f a
pure [a]
d) (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([a]
d forall a. [a] -> [a] -> [a]
++) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe String -> m [a]
go forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack forall b c a. (b -> c) -> (a -> b) -> a -> c
. NextPage -> Text
npOffset) Maybe NextPage
mNextPage

-- | Get a single resource
getSingle
  :: ( MonadUnliftIO m
     , MonadLogger m
     , MonadReader env m
     , HasAsanaAccessKey env
     , FromJSON a
     )
  => String
  -> m a
getSingle :: forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, FromJSON a) =>
String -> m a
getSingle String
path = forall a. Single a -> a
sData forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, FromJSON a) =>
String -> [(String, String)] -> Int -> Maybe String -> m a
get String
path [] Int
1 forall a. Maybe a
Nothing

get
  :: ( MonadUnliftIO m
     , MonadLogger m
     , MonadReader env m
     , HasAsanaAccessKey env
     , FromJSON a
     )
  => String
  -> [(String, String)]
  -> Int
  -> Maybe String
  -> m a
get :: forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, FromJSON a) =>
String -> [(String, String)] -> Int -> Maybe String -> m a
get String
path [(String, String)]
params Int
limit Maybe String
mOffset = do
  AsanaAccessKey Text
key <- forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view forall env. HasAsanaAccessKey env => Lens' env AsanaAccessKey
asanaAccessKeyL
  let
    request :: Request
request =
      String -> Request
parseRequest_
        forall a b. (a -> b) -> a -> b
$ String
"https://app.asana.com/api/1.0"
        forall a. Semigroup a => a -> a -> a
<> String
path
        forall a. Semigroup a => a -> a -> a
<> String
"?limit="
        forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show Int
limit -- Ignored on not paging responses
        forall a. Semigroup a => a -> a -> a
<> forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
"" (String
"&offset=" forall a. Semigroup a => a -> a -> a
<>) Maybe String
mOffset
        forall a. Semigroup a => a -> a -> a
<> forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\(String
k, String
v) -> String
"&" forall a. Semigroup a => a -> a -> a
<> String
k forall a. Semigroup a => a -> a -> a
<> String
"=" forall a. Semigroup a => a -> a -> a
<> String
v) [(String, String)]
params
  Response a
response <- forall a (m :: * -> *).
(MonadUnliftIO m, MonadLogger m) =>
Int -> m (Response a) -> m (Response a)
retry Int
50 forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a.
(MonadIO m, FromJSON a) =>
Request -> m (Response a)
httpJSON (Text -> Request -> Request
addAuthorization Text
key Request
request)
  forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
300 forall a. Ord a => a -> a -> Bool
<= forall a. Response a -> Int
getResponseStatusCode Response a
response)
    forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *). MonadLogger m => Text -> Text -> m ()
logWarnNS Text
"Asana"
    forall a b. (a -> b) -> a -> b
$ Text
"GET failed, status: "
    forall a. Semigroup a => a -> a -> a
<> String -> Text
pack (forall a. Show a => a -> String
show forall a b. (a -> b) -> a -> b
$ forall a. Response a -> Int
getResponseStatusCode Response a
response)
  forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a. Response a -> a
getResponseBody Response a
response

put
  :: ( MonadUnliftIO m
     , MonadLogger m
     , MonadReader env m
     , HasAsanaAccessKey env
     , ToJSON a
     )
  => String
  -> a
  -> m Value
put :: forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, ToJSON a) =>
String -> a -> m Value
put = forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, ToJSON a) =>
ByteString -> String -> a -> m Value
httpAction ByteString
"PUT"

post
  :: ( MonadUnliftIO m
     , MonadLogger m
     , MonadReader env m
     , HasAsanaAccessKey env
     , ToJSON a
     )
  => String
  -> a
  -> m Value
post :: forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, ToJSON a) =>
String -> a -> m Value
post = forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, ToJSON a) =>
ByteString -> String -> a -> m Value
httpAction ByteString
"POST"

httpAction
  :: ( MonadUnliftIO m
     , MonadLogger m
     , MonadReader env m
     , HasAsanaAccessKey env
     , ToJSON a
     )
  => ByteString
  -> String
  -> a
  -> m Value
httpAction :: forall (m :: * -> *) env a.
(MonadUnliftIO m, MonadLogger m, MonadReader env m,
 HasAsanaAccessKey env, ToJSON a) =>
ByteString -> String -> a -> m Value
httpAction ByteString
verb String
path a
payload = do
  AsanaAccessKey Text
key <- forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view forall env. HasAsanaAccessKey env => Lens' env AsanaAccessKey
asanaAccessKeyL
  let request :: Request
request = String -> Request
parseRequest_ forall a b. (a -> b) -> a -> b
$ String
"https://app.asana.com/api/1.0" forall a. Semigroup a => a -> a -> a
<> String
path

  Response Value
response <- forall a (m :: * -> *).
(MonadUnliftIO m, MonadLogger m) =>
Int -> m (Response a) -> m (Response a)
retry Int
10 forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a.
(MonadIO m, FromJSON a) =>
Request -> m (Response a)
httpJSON
    (ByteString -> Request -> Request
setRequestMethod ByteString
verb forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ToJSON a => a -> Request -> Request
setRequestBodyJSON a
payload forall a b. (a -> b) -> a -> b
$ Text -> Request -> Request
addAuthorization
      Text
key
      Request
request
    )
  forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
300 forall a. Ord a => a -> a -> Bool
<= forall a. Response a -> Int
getResponseStatusCode Response Value
response) forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *). MonadLogger m => Text -> Text -> m ()
logWarnNS Text
"Asana" forall a b. (a -> b) -> a -> b
$ forall a. Monoid a => [a] -> a
mconcat
    [ Text
"Request failed"
    , Text
"\n  method: " forall a. Semigroup a => a -> a -> a
<> ByteString -> Text
T.decodeUtf8 ByteString
verb
    , Text
"\n  status: " forall a. Semigroup a => a -> a -> a
<> String -> Text
pack (forall a. Show a => a -> String
show forall a b. (a -> b) -> a -> b
$ forall a. Response a -> Int
getResponseStatusCode Response Value
response)
    , Text
"\n  body  : " forall a. Semigroup a => a -> a -> a
<> ByteString -> Text
T.decodeUtf8
      (ByteString -> ByteString
BSL.toStrict forall a b. (a -> b) -> a -> b
$ forall a. ToJSON a => a -> ByteString
encode forall a b. (a -> b) -> a -> b
$ forall a. ToJSON a => a -> Value
toJSON forall a b. (a -> b) -> a -> b
$ forall a. Response a -> a
getResponseBody @Value Response Value
response)
    ]

  forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a. Response a -> a
getResponseBody Response Value
response

addAuthorization :: Text -> Request -> Request
addAuthorization :: Text -> Request -> Request
addAuthorization Text
key =
  HeaderName -> ByteString -> Request -> Request
addRequestHeader HeaderName
"Authorization" forall a b. (a -> b) -> a -> b
$ ByteString
"Bearer " forall a. Semigroup a => a -> a -> a
<> Text -> ByteString
T.encodeUtf8 Text
key

retry
  :: forall a m
   . (MonadUnliftIO m, MonadLogger m)
  => Int
  -> m (Response a)
  -> m (Response a)
retry :: forall a (m :: * -> *).
(MonadUnliftIO m, MonadLogger m) =>
Int -> m (Response a) -> m (Response a)
retry Int
attempt m (Response a)
go
  | Int
attempt forall a. Ord a => a -> a -> Bool
<= Int
0 = m (Response a)
go
  | Bool
otherwise = Response a -> m (Response a)
handler forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< m (Response a)
go forall (m :: * -> *) e a.
(MonadUnliftIO m, Exception e) =>
m a -> (e -> m a) -> m a
`catch` JSONException -> m (Response a)
handleParseError
 where
  handleParseError :: JSONException -> m (Response a)
  handleParseError :: JSONException -> m (Response a)
handleParseError JSONException
e = case JSONException
e of
    JSONParseException Request
_ Response ()
rsp ParseError
_ -> forall e b. Exception e => e -> Response b -> m (Response a)
orThrow JSONException
e Response ()
rsp
    JSONConversionException Request
_ Response Value
rsp String
_ -> forall e b. Exception e => e -> Response b -> m (Response a)
orThrow JSONException
e Response Value
rsp

  orThrow :: Exception e => e -> Response b -> m (Response a)
  orThrow :: forall e b. Exception e => e -> Response b -> m (Response a)
orThrow e
e Response b
response
    | forall a. Response a -> Int
getResponseStatusCode Response b
response forall a. Eq a => a -> a -> Bool
== Int
429 = do
      let seconds :: Int
seconds = forall a. Response a -> Int
getResponseDelay Response b
response
      forall (m :: * -> *). MonadLogger m => Text -> Text -> m ()
logWarnNS Text
"Asana" forall a b. (a -> b) -> a -> b
$ Text
"Retrying after " forall a. Semigroup a => a -> a -> a
<> String -> Text
pack (forall a. Show a => a -> String
show Int
seconds) forall a. Semigroup a => a -> a -> a
<> Text
" seconds"
      forall (m :: * -> *). MonadIO m => Int -> m ()
threadDelay forall a b. (a -> b) -> a -> b
$ Int
seconds forall a. Num a => a -> a -> a
* Int
1000000
      forall a (m :: * -> *).
(MonadUnliftIO m, MonadLogger m) =>
Int -> m (Response a) -> m (Response a)
retry (forall a. Enum a => a -> a
pred Int
attempt) m (Response a)
go
    | Bool
otherwise = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwIO e
e

  handler :: Response a -> m (Response a)
  handler :: Response a -> m (Response a)
handler Response a
response
    | forall a. Response a -> Int
getResponseStatusCode Response a
response forall a. Eq a => a -> a -> Bool
== Int
429 = do
      let seconds :: Int
seconds = forall a. Response a -> Int
getResponseDelay Response a
response
      forall (m :: * -> *). MonadLogger m => Text -> Text -> m ()
logWarnNS Text
"Asana" forall a b. (a -> b) -> a -> b
$ Text
"Retrying after " forall a. Semigroup a => a -> a -> a
<> String -> Text
pack (forall a. Show a => a -> String
show Int
seconds) forall a. Semigroup a => a -> a -> a
<> Text
" seconds"
      forall (m :: * -> *). MonadIO m => Int -> m ()
threadDelay forall a b. (a -> b) -> a -> b
$ Int
seconds forall a. Num a => a -> a -> a
* Int
100000
      forall a (m :: * -> *).
(MonadUnliftIO m, MonadLogger m) =>
Int -> m (Response a) -> m (Response a)
retry (forall a. Enum a => a -> a
pred Int
attempt) m (Response a)
go
    | Bool
otherwise = forall (f :: * -> *) a. Applicative f => a -> f a
pure Response a
response

getResponseDelay :: Response a -> Int
getResponseDelay :: forall a. Response a -> Int
getResponseDelay =
  forall a. a -> Maybe a -> a
fromMaybe Int
0
    forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Read a => String -> Maybe a
readMaybe
    forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
T.unpack
    forall b c a. (b -> c) -> (a -> b) -> a -> c
. OnDecodeError -> ByteString -> Text
T.decodeUtf8With OnDecodeError
T.lenientDecode
    forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Monoid a => [a] -> a
mconcat
    forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. HeaderName -> Response a -> [ByteString]
getResponseHeader HeaderName
"Retry-After"