module Aws.Lambda.Runtime.ApiInfo
  ( Event (..),
    fetchEvent,
  )
where

import qualified Aws.Lambda.Runtime.API.Endpoints as Endpoints
import qualified Aws.Lambda.Runtime.Error as Error
import Control.Exception (IOException)
import Control.Exception.Safe.Checked
import qualified Control.Monad as Monad
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as ByteString
import qualified Data.ByteString.Lazy.Char8 as Lazy
import Data.Text (Text, pack)
import qualified Data.Text as Text
import qualified Network.HTTP.Client as Http
import qualified Network.HTTP.Types.Header as Http
import qualified Text.Read as Read

-- | Event that is fetched out of the AWS Lambda API
data Event = Event
  { Event -> Int
deadlineMs :: !Int,
    Event -> Text
traceId :: !Text,
    Event -> Text
awsRequestId :: !Text,
    Event -> Text
invokedFunctionArn :: !Text,
    Event -> ByteString
event :: !Lazy.ByteString
  }
  deriving (Int -> Event -> ShowS
[Event] -> ShowS
Event -> String
(Int -> Event -> ShowS)
-> (Event -> String) -> ([Event] -> ShowS) -> Show Event
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Event] -> ShowS
$cshowList :: [Event] -> ShowS
show :: Event -> String
$cshow :: Event -> String
showsPrec :: Int -> Event -> ShowS
$cshowsPrec :: Int -> Event -> ShowS
Show)

-- | Performs a GET to the endpoint that provides the next event
fetchEvent :: Throws Error.Parsing => Http.Manager -> Text -> IO Event
fetchEvent :: Manager -> Text -> IO Event
fetchEvent Manager
manager Text
lambdaApi = do
  Response ByteString
response <- Manager -> Text -> IO (Response ByteString)
fetchApiData Manager
manager Text
lambdaApi
  let body :: ByteString
body = Response ByteString -> ByteString
forall body. Response body -> body
Http.responseBody Response ByteString
response
      headers :: ResponseHeaders
headers = Response ByteString -> ResponseHeaders
forall body. Response body -> ResponseHeaders
Http.responseHeaders Response ByteString
response
  (Event -> (HeaderName, ByteString) -> IO Event)
-> Event -> ResponseHeaders -> IO Event
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
Monad.foldM Throws Parsing => Event -> (HeaderName, ByteString) -> IO Event
Event -> (HeaderName, ByteString) -> IO Event
reduceEvent (ByteString -> Event
initialEvent ByteString
body) ResponseHeaders
headers

fetchApiData :: Http.Manager -> Text -> IO (Http.Response Lazy.ByteString)
fetchApiData :: Manager -> Text -> IO (Response ByteString)
fetchApiData Manager
manager Text
lambdaApi = do
  let Endpoints.Endpoint Text
endpoint = Text -> Endpoint
Endpoints.nextInvocation Text
lambdaApi
  Request
request <- String -> IO Request
forall (m :: * -> *). MonadThrow m => String -> m Request
Http.parseRequest (String -> IO Request) -> (Text -> String) -> Text -> IO Request
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
Text.unpack (Text -> IO Request) -> Text -> IO Request
forall a b. (a -> b) -> a -> b
$ Text
endpoint
  IO (Response ByteString) -> IO (Response ByteString)
keepRetrying (IO (Response ByteString) -> IO (Response ByteString))
-> IO (Response ByteString) -> IO (Response ByteString)
forall a b. (a -> b) -> a -> b
$ Request -> Manager -> IO (Response ByteString)
Http.httpLbs Request
request Manager
manager

reduceEvent :: Throws Error.Parsing => Event -> (Http.HeaderName, ByteString) -> IO Event
reduceEvent :: Event -> (HeaderName, ByteString) -> IO Event
reduceEvent Event
event (HeaderName, ByteString)
header =
  case (HeaderName, ByteString)
header of
    (HeaderName
"Lambda-Runtime-Deadline-Ms", ByteString
value) ->
      case String -> Maybe Int
forall a. Read a => String -> Maybe a
Read.readMaybe (String -> Maybe Int) -> String -> Maybe Int
forall a b. (a -> b) -> a -> b
$ ByteString -> String
ByteString.unpack ByteString
value of
        Just Int
ms -> Event -> IO Event
forall (f :: * -> *) a. Applicative f => a -> f a
pure Event
event {deadlineMs :: Int
deadlineMs = Int
ms}
        Maybe Int
Nothing -> Parsing -> IO Event
forall (m :: * -> *) e a.
(MonadThrow m, Exception e, Throws e) =>
e -> m a
throw (Text -> Text -> Text -> Parsing
Error.Parsing Text
"Could not parse deadlineMs." (String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> String
ByteString.unpack ByteString
value) Text
"deadlineMs")
    (HeaderName
"Lambda-Runtime-Trace-Id", ByteString
value) ->
      Event -> IO Event
forall (f :: * -> *) a. Applicative f => a -> f a
pure Event
event {traceId :: Text
traceId = String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> String
ByteString.unpack ByteString
value}
    (HeaderName
"Lambda-Runtime-Aws-Request-Id", ByteString
value) ->
      Event -> IO Event
forall (f :: * -> *) a. Applicative f => a -> f a
pure Event
event {awsRequestId :: Text
awsRequestId = String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> String
ByteString.unpack ByteString
value}
    (HeaderName
"Lambda-Runtime-Invoked-Function-Arn", ByteString
value) ->
      Event -> IO Event
forall (f :: * -> *) a. Applicative f => a -> f a
pure Event
event {invokedFunctionArn :: Text
invokedFunctionArn = String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> String
ByteString.unpack ByteString
value}
    (HeaderName, ByteString)
_ ->
      Event -> IO Event
forall (f :: * -> *) a. Applicative f => a -> f a
pure Event
event

initialEvent :: Lazy.ByteString -> Event
initialEvent :: ByteString -> Event
initialEvent ByteString
body =
  Event :: Int -> Text -> Text -> Text -> ByteString -> Event
Event
    { deadlineMs :: Int
deadlineMs = Int
0,
      traceId :: Text
traceId = Text
"",
      awsRequestId :: Text
awsRequestId = Text
"",
      invokedFunctionArn :: Text
invokedFunctionArn = Text
"",
      event :: ByteString
event = ByteString
body
    }

keepRetrying :: IO (Http.Response Lazy.ByteString) -> IO (Http.Response Lazy.ByteString)
keepRetrying :: IO (Response ByteString) -> IO (Response ByteString)
keepRetrying IO (Response ByteString)
action = do
  Either IOException (Response ByteString)
result <- (Throws IOException => IO (Response ByteString))
-> IO (Either IOException (Response ByteString))
forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
(Throws e => m a) -> m (Either e a)
try IO (Response ByteString)
Throws IOException => IO (Response ByteString)
action :: IO (Either IOException (Http.Response Lazy.ByteString))
  case Either IOException (Response ByteString)
result of
    Right Response ByteString
success -> Response ByteString -> IO (Response ByteString)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Response ByteString
success
    Either IOException (Response ByteString)
_ -> IO (Response ByteString) -> IO (Response ByteString)
keepRetrying IO (Response ByteString)
action