packages feed

aws-lambda-haskell-runtime 2.0.6 → 3.0.0

raw patch · 10 files changed

+187/−111 lines, 10 files

Files

aws-lambda-haskell-runtime.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: eab5b338b49db7854613ecd43b17245f105c8c936c40230ecf9d08c5dfad9d6d+-- hash: c21b3c988844f7c4bfb806aff4e307c5d5dc63a7973eff4a83eb329e7bccb1c9  name:           aws-lambda-haskell-runtime-version:        2.0.6+version:        3.0.0 synopsis:       Haskell runtime for AWS Lambda description:    Please see the README on GitHub at <https://github.com/theam/aws-lambda-haskell-runtime#readme> category:       AWS@@ -46,6 +46,7 @@       Aws.Lambda.Runtime.Environment       Aws.Lambda.Runtime.Error       Aws.Lambda.Runtime.Publish+      Aws.Lambda.Utilities       Paths_aws_lambda_haskell_runtime   hs-source-dirs:       src
src/Aws/Lambda/Configuration.hs view
@@ -3,7 +3,6 @@   ( Main.LambdaOptions(..)   , generateLambdaDispatcher   , Dispatch.decodeObj-  , Dispatch.encodeObj   ) where 
src/Aws/Lambda/Meta/Dispatch.hs view
@@ -1,50 +1,41 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE BangPatterns #-}  {-| Dispatcher generation -} module Aws.Lambda.Meta.Dispatch   ( generate   , decodeObj-  , encodeObj   , Runtime.LambdaResult(..)   ) where +import qualified Data.Char as Char import Data.Function ((&)) import Data.Text (Text) import qualified Data.Text as Text-import qualified Data.Char as Char  import Data.Aeson import qualified Data.ByteString.Lazy.Char8 as LazyByteString import qualified Language.Haskell.TH as Meta  import Aws.Lambda.Meta.Common+import qualified Aws.Lambda.Meta.Main as Main+import qualified Aws.Lambda.Runtime.ApiGatewayInfo as ApiGatewayInfo+import Aws.Lambda.Runtime.Common (toStandaloneLambdaResponse) import qualified Aws.Lambda.Runtime.Common as Runtime import qualified Aws.Lambda.Runtime.Error as Error-import qualified Aws.Lambda.Runtime.ApiGatewayInfo as ApiGatewayInfo import qualified Control.Exception as Unchecked-import qualified Aws.Lambda.Meta.Main as Main-import Data.Typeable (Typeable, typeRep, Proxy (..))+import Data.Typeable (Proxy (..), Typeable, typeRep)  {-| Helper function that the dispatcher will use to decode the JSON that comes as an AWS Lambda event into the appropriate type expected by the handler. -}-decodeObj :: forall a. (FromJSON a, Typeable a) => String -> Either Error.Parsing a+decodeObj :: forall a. (FromJSON a, Typeable a) => LazyByteString.ByteString -> Either Error.Parsing a decodeObj x =   let objName = show (typeRep (Proxy :: Proxy a)) in-  case (eitherDecode $ LazyByteString.pack x) of-    Left e  -> Left $ Error.Parsing e x objName+  case (eitherDecode x) of+    Left e  -> Left $ Error.Parsing e (LazyByteString.unpack x) objName     Right v -> return v -{-| Helper function that the dispatcher will use to-decode the JSON that comes as an AWS Lambda event into the-appropriate type expected by the handler.--}-encodeObj :: ToJSON a => a -> String-encodeObj x = LazyByteString.unpack (encode x)-- {-| Generates the dispatcher out of a list of handler names in the form @src/Foo/Bar.handler@ @@ -70,20 +61,18 @@   let pat = Meta.LitP (Meta.StringL $ Text.unpack lambdaHandler)   body <- [e|do     case decodeObj $(expressionName "eventObject") of-      Right eventObject -> case decodeObj $(expressionName "contextObject") of-        Right context -> (do-          result <- $(expressionName (qualifiedHandlerName lambdaHandler)) eventObject context-          either (pure . Left . Runtime.StandaloneLambdaError . encodeObj) (pure . Right . Runtime.StandaloneLambdaResult . encodeObj) result)-          `Unchecked.catch` \(handlerError :: Unchecked.SomeException) -> pure . Left . Runtime.StandaloneLambdaError . encodeObj . show $ handlerError-        Left err -> pure . Left . Runtime.StandaloneLambdaError . encodeObj $ err-      Left err -> pure . Left . Runtime.StandaloneLambdaError . encodeObj $ err|]+      Right eventObject -> (do+          result <- $(expressionName (qualifiedHandlerName lambdaHandler)) eventObject contextObject+          either (pure . Left . Runtime.StandaloneLambdaError . toStandaloneLambdaResponse) (pure . Right . Runtime.StandaloneLambdaResult . toStandaloneLambdaResponse) result)+          `Unchecked.catch` \(handlerError :: Unchecked.SomeException) -> pure . Left . Runtime.StandaloneLambdaError . toStandaloneLambdaResponse . show $ handlerError+      Left err -> pure . Left . Runtime.StandaloneLambdaError . toStandaloneLambdaResponse $ err|]   pure $ Meta.Match pat (Meta.NormalB body) []  standaloneLambdaUnmatchedCase :: Meta.MatchQ standaloneLambdaUnmatchedCase = do   let pattern = Meta.WildP   body <- [e|-    pure . Left . Runtime.StandaloneLambdaError . encodeObj $ ("Handler " <> $(expressionName "functionHandler") <> " does not exist on project")+    pure . Left . Runtime.StandaloneLambdaError . toStandaloneLambdaResponse $ ("Handler " <> $(expressionName "functionHandler") <> " does not exist on project" :: String)     |]   pure $ Meta.Match pattern (Meta.NormalB body) [] @@ -93,25 +82,23 @@   body <- [e|do     let returnErr statusCode = pure . Left . Runtime.ApiGatewayLambdaError . ApiGatewayInfo.mkApiGatewayResponse statusCode     case decodeObj $(expressionName "eventObject") of-      Right eventObject -> case decodeObj $(expressionName "contextObject") of-        Right context -> do-          resultE <- Unchecked.try $ $(expressionName (qualifiedHandlerName lambdaHandler)) eventObject context-          case resultE of-            Right result ->-              either (pure . Left . Runtime.ApiGatewayLambdaError . fmap toJSON) (pure . Right . Runtime.ApiGatewayResult . fmap toJSON) result-            Left (handlerError :: Unchecked.SomeException) ->-              if (Runtime.propagateImpureExceptions . Runtime.apiGatewayDispatcherOptions $ options)-              then returnErr 500 . toJSON . show $ handlerError-              else returnErr 500 . toJSON $ "Something went wrong."-        Left err -> returnErr 500 . toJSON $ err-      Left err -> returnErr 400 . toJSON $ err|]+      Right eventObject -> do+        resultE <- Unchecked.try $ $(expressionName (qualifiedHandlerName lambdaHandler)) eventObject contextObject+        case resultE of+          Right result ->+            either (pure . Left . Runtime.ApiGatewayLambdaError . fmap toApiGatewayResponseBody) (pure . Right . Runtime.ApiGatewayResult . fmap toApiGatewayResponseBody) result+          Left (handlerError :: Unchecked.SomeException) ->+            if (Runtime.propagateImpureExceptions . Runtime.apiGatewayDispatcherOptions $ options)+            then returnErr 500 . toApiGatewayResponseBody . show $ handlerError+            else returnErr 500 . toApiGatewayResponseBody . Text.pack $ "Something went wrong."+      Left err -> returnErr 400 . toApiGatewayResponseBody . show $ err|]   pure $ Meta.Match pat (Meta.NormalB body) []  apiGatewayUnmatchedCase :: Meta.MatchQ apiGatewayUnmatchedCase = do   let pattern = Meta.WildP   body <- [e|-    pure . Left . Runtime.ApiGatewayLambdaError . ApiGatewayInfo.mkApiGatewayResponse 500 . toJSON $ ("Handler " <> $(expressionName "functionHandler") <> " does not exist on project")+    pure . Left . Runtime.ApiGatewayLambdaError . ApiGatewayInfo.mkApiGatewayResponse 500 . toApiGatewayResponseBody $ ("Handler " <> $(expressionName "functionHandler") <> " does not exist on project")     |]   pure $ Meta.Match pattern (Meta.NormalB body) [] 
src/Aws/Lambda/Meta/Main.hs view
@@ -21,5 +21,5 @@  where   directCallBody =     [e|-    runLambda run+    runLambda initializeContext run     |]
src/Aws/Lambda/Runtime.hs view
@@ -1,3 +1,6 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications    #-}+ module Aws.Lambda.Runtime   ( runLambda   , Runtime.LambdaResult(..)@@ -15,7 +18,7 @@ import qualified Network.HTTP.Client as Http  import Data.Aeson-import qualified Data.ByteString.Lazy.Char8 as LazyByteString+import Data.IORef  import qualified Aws.Lambda.Runtime.ApiInfo as ApiInfo import qualified Aws.Lambda.Runtime.Common as Runtime@@ -27,13 +30,15 @@  -- | Runs the user @haskell_lambda@ executable and posts back the -- results. This is called from the layer's @main@ function.-runLambda :: Runtime.RunCallback -> IO ()-runLambda callback = do+runLambda :: forall context. IO context -> Runtime.RunCallback context -> IO ()+runLambda initializeCustomContext callback = do   manager <- Http.newManager httpManagerSettings+  customContext <- initializeCustomContext+  customContextRef <- newIORef customContext   forever $ do     lambdaApi <- Environment.apiEndpoint `catch` variableNotSet     event     <- ApiInfo.fetchEvent manager lambdaApi `catch` errorParsing-    context   <- Context.initialize event `catch` errorParsing `catch` variableNotSet+    context   <- Context.initialize @context customContextRef event `catch` errorParsing `catch` variableNotSet      (((invokeAndRun callback manager lambdaApi event context       `Checked.catch` \err -> Publish.parsingError err lambdaApi context manager)@@ -51,11 +56,11 @@ invokeAndRun   :: Throws Error.Invocation   => Throws Error.EnvironmentVariableNotSet-  => Runtime.RunCallback+  => Runtime.RunCallback context   -> Http.Manager   -> String   -> ApiInfo.Event-  -> Context.Context+  -> Context.Context context   -> IO () invokeAndRun callback manager lambdaApi event context = do   result <- invokeWithCallback callback event context@@ -66,17 +71,17 @@ invokeWithCallback   :: Throws Error.Invocation   => Throws Error.EnvironmentVariableNotSet-  => Runtime.RunCallback+  => Runtime.RunCallback context   -> ApiInfo.Event-  -> Context.Context+  -> Context.Context context   -> IO Runtime.LambdaResult invokeWithCallback callback event context = do   handlerName <- Environment.handlerName   let lambdaOptions = Runtime.LambdaOptions-                      { eventObject = LazyByteString.unpack $ ApiInfo.event event-                      , contextObject = LazyByteString.unpack . encode $ context+                      { eventObject = ApiInfo.event event                       , functionHandler = handlerName                       , executionUuid = ""  -- DirectCall doesnt use UUID+                      , contextObject = context                       }   result <- callback lambdaOptions   -- Flush output to insure output goes into CloudWatch logs
src/Aws/Lambda/Runtime/ApiGatewayInfo.hs view
@@ -1,23 +1,30 @@-{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE DefaultSignatures          #-}+{-# LANGUAGE DerivingStrategies         #-}+{-# LANGUAGE DuplicateRecordFields      #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GeneralisedNewtypeDeriving #-}+{-# LANGUAGE UndecidableInstances       #-}  module Aws.Lambda.Runtime.ApiGatewayInfo   ( ApiGatewayRequest(..)   , ApiGatewayRequestContext(..)   , ApiGatewayRequestContextIdentity(..)   , ApiGatewayResponse(..)+  , ApiGatewayResponseBody(..)+  , ToApiGatewayResponseBody(..)   , mkApiGatewayResponse ) where +import Aws.Lambda.Utilities import Data.Aeson+import Data.Aeson.Types (Parser)+import qualified Data.Aeson.Types as T+import qualified Data.CaseInsensitive as CI import Data.HashMap.Strict (HashMap) import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Encoding as T import GHC.Generics (Generic) import Network.HTTP.Types-import qualified Data.CaseInsensitive as CI-import qualified Data.Aeson.Types as T-import qualified Data.ByteString.Lazy.Char8 as LazyByteString-import Data.Aeson.Types (Parser)  data ApiGatewayRequest body = ApiGatewayRequest   { apiGatewayRequestResource              :: !Text@@ -32,20 +39,43 @@   , apiGatewayRequestBody                  :: !(Maybe body)   } deriving (Show) +-- We special case String and Text in order+-- to avoid unneeded encoding which will wrap them in quotes and break parsing+instance {-# OVERLAPPING #-} FromJSON (ApiGatewayRequest Text) where+  parseJSON = parseApiGatewayRequest (.:)++instance {-# OVERLAPPING #-} FromJSON (ApiGatewayRequest String) where+  parseJSON = parseApiGatewayRequest (.:)+ instance FromJSON body => FromJSON (ApiGatewayRequest body) where-  parseJSON (Object v) = ApiGatewayRequest <$>-    v .: "resource" <*>-    v .: "path" <*>-    v .: "httpMethod" <*>-    v .: "headers" <*>-    v .: "queryStringParameters" <*>-    v .: "pathParameters" <*>-    v .: "stageVariables" <*>-    v .: "isBase64Encoded" <*>-    v .: "requestContext" <*>-    v `parseObjectFromStringField` "body"-  parseJSON _ = fail "Expected ApiGatewayRequest to be an object."+  parseJSON = parseApiGatewayRequest parseObjectFromStringField +-- We need this because API Gateway is going to send us the payload as a JSON string+parseObjectFromStringField :: FromJSON a => Object -> Text -> Parser (Maybe a)+parseObjectFromStringField obj fieldName = do+  fieldContents <- obj .: fieldName+  case fieldContents of+    String stringContents ->+      case eitherDecodeStrict (T.encodeUtf8 stringContents) of+        Right success -> pure success+        Left err      -> fail err+    Null -> pure Nothing+    other -> T.typeMismatch "String or Null" other++parseApiGatewayRequest :: (Object -> Text -> Parser (Maybe body)) -> Value -> Parser (ApiGatewayRequest body)+parseApiGatewayRequest bodyParser (Object v) = ApiGatewayRequest <$>+  v .: "resource" <*>+  v .: "path" <*>+  v .: "httpMethod" <*>+  v .: "headers" <*>+  v .: "queryStringParameters" <*>+  v .: "pathParameters" <*>+  v .: "stageVariables" <*>+  v .: "isBase64Encoded" <*>+  v .: "requestContext" <*>+  v `bodyParser` "body"+parseApiGatewayRequest _ _ = fail "Expected ApiGatewayRequest to be an object."+ data ApiGatewayRequestContext = ApiGatewayRequestContext   { apiGatewayRequestContextResourceId        :: !Text   , apiGatewayRequestContextResourcePath      :: !Text@@ -112,18 +142,23 @@     v .: "user"   parseJSON _ = fail "Expected ApiGatewayRequestContextIdentity to be an object." --- We need this because API Gateway is going to send us the payload as a JSON string-parseObjectFromStringField :: FromJSON a => Object -> Text -> Parser (Maybe a)-parseObjectFromStringField obj fieldName = do-  fieldContents <- obj .: fieldName-  case fieldContents of-    String stringContents ->-      case eitherDecodeStrict (T.encodeUtf8 stringContents) of-        Right success -> pure success-        Left err -> fail err-    Null -> pure Nothing-    other -> T.typeMismatch "String or Null" other+newtype ApiGatewayResponseBody =+  ApiGatewayResponseBody Text+  deriving newtype (ToJSON, FromJSON) +class ToApiGatewayResponseBody a where+  toApiGatewayResponseBody :: a -> ApiGatewayResponseBody++-- We special case Text and String to avoid unneeded encoding which will wrap them in quotes+instance {-# OVERLAPPING #-} ToApiGatewayResponseBody Text where+  toApiGatewayResponseBody = ApiGatewayResponseBody++instance {-# OVERLAPPING #-} ToApiGatewayResponseBody String where+  toApiGatewayResponseBody = ApiGatewayResponseBody . T.pack++instance ToJSON a => ToApiGatewayResponseBody a where+  toApiGatewayResponseBody = ApiGatewayResponseBody . toJSONText+ data ApiGatewayResponse body = ApiGatewayResponse   { apiGatewayResponseStatusCode      :: !Int   , apiGatewayResponseHeaders         :: !ResponseHeaders@@ -135,12 +170,15 @@   fmap f v = v { apiGatewayResponseBody = f (apiGatewayResponseBody v) }  instance ToJSON body => ToJSON (ApiGatewayResponse body)  where-  toJSON ApiGatewayResponse {..} = object-    [ "statusCode" .= apiGatewayResponseStatusCode-    , "body" .= (String . T.pack . LazyByteString.unpack . encode @body $ apiGatewayResponseBody)-    , "headers" .= object (map headerToPair apiGatewayResponseHeaders)-    , "isBase64Encoded" .= apiGatewayResponseIsBase64Encoded-    ]+  toJSON = apiGatewayResponseToJSON toJSON++apiGatewayResponseToJSON :: (body -> Value) -> ApiGatewayResponse body -> Value+apiGatewayResponseToJSON bodyTransformer ApiGatewayResponse {..} = object+  [ "statusCode" .= apiGatewayResponseStatusCode+  , "body" .= bodyTransformer apiGatewayResponseBody+  , "headers" .= object (map headerToPair apiGatewayResponseHeaders)+  , "isBase64Encoded" .= apiGatewayResponseIsBase64Encoded+  ]  mkApiGatewayResponse :: Int -> payload -> ApiGatewayResponse payload mkApiGatewayResponse code payload =
src/Aws/Lambda/Runtime/Common.hs view
@@ -1,4 +1,8 @@-{-# LANGUAGE DeriveLift #-}+{-# LANGUAGE DeriveLift                 #-}+{-# LANGUAGE DerivingStrategies         #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE UndecidableInstances       #-}  module Aws.Lambda.Runtime.Common   ( RunCallback@@ -8,11 +12,18 @@   , DispatcherOptions(..)   , ApiGatewayDispatcherOptions(..)   , DispatcherStrategy(..)+  , ToLambdaResponseBody(..)+  , unLambdaResponseBody   , defaultDispatcherOptions   ) where  import Aws.Lambda.Runtime.ApiGatewayInfo-import Data.Aeson (Value)+import Aws.Lambda.Runtime.Context (Context)+import Aws.Lambda.Utilities+import Data.Aeson (FromJSON, ToJSON)+import qualified Data.ByteString.Lazy as Lazy+import Data.Text (Text)+import qualified Data.Text as Text import GHC.Generics (Generic) import Language.Haskell.TH.Syntax (Lift) @@ -38,23 +49,42 @@   deriving (Lift)  -- | Callback that we pass to the dispatcher function-type RunCallback =-  LambdaOptions -> IO (Either LambdaError LambdaResult)+type RunCallback context =+  LambdaOptions context -> IO (Either LambdaError LambdaResult) +-- | Wrapper type for lambda response body+newtype LambdaResponseBody =+  LambdaResponseBody { unLambdaResponseBody :: Text }+  deriving newtype (ToJSON, FromJSON)++class ToLambdaResponseBody a where+  toStandaloneLambdaResponse :: a -> LambdaResponseBody++-- We need to special case String and Text to avoid unneeded encoding+-- which results in extra quotes put around plain text responses+instance {-# OVERLAPPING #-} ToLambdaResponseBody String where+  toStandaloneLambdaResponse = LambdaResponseBody . Text.pack++instance {-# OVERLAPPING #-} ToLambdaResponseBody Text where+  toStandaloneLambdaResponse = LambdaResponseBody++instance ToJSON a => ToLambdaResponseBody a where+  toStandaloneLambdaResponse = LambdaResponseBody . toJSONText+ -- | Wrapper type for lambda execution results data LambdaError =-  StandaloneLambdaError String |-  ApiGatewayLambdaError (ApiGatewayResponse Value)+  StandaloneLambdaError LambdaResponseBody |+  ApiGatewayLambdaError (ApiGatewayResponse ApiGatewayResponseBody)  -- | Wrapper type to handle the result of the user data LambdaResult =-  StandaloneLambdaResult String |-  ApiGatewayResult (ApiGatewayResponse Value)+  StandaloneLambdaResult LambdaResponseBody |+  ApiGatewayResult (ApiGatewayResponse ApiGatewayResponseBody)  -- | Options that the generated main expects-data LambdaOptions = LambdaOptions-  { eventObject     :: !String-  , contextObject   :: !String+data LambdaOptions context = LambdaOptions+  { eventObject     :: !Lazy.ByteString   , functionHandler :: !String   , executionUuid   :: !String+  , contextObject   :: !(Context context)   } deriving (Generic)
src/Aws/Lambda/Runtime/Context.hs view
@@ -4,15 +4,14 @@   ) where  import Control.Exception.Safe.Checked-import Data.Aeson (FromJSON (..), ToJSON (..))-import GHC.Generics (Generic)+import Data.IORef  import qualified Aws.Lambda.Runtime.ApiInfo as ApiInfo import qualified Aws.Lambda.Runtime.Environment as Environment import qualified Aws.Lambda.Runtime.Error as Error  -- | Context that is passed to all the handlers-data Context = Context+data Context context = Context   { memoryLimitInMb    :: !Int   , functionName       :: !String   , functionVersion    :: !String@@ -22,16 +21,23 @@   , logStreamName      :: !String   , logGroupName       :: !String   , deadline           :: !Int-  } deriving (Generic, FromJSON, ToJSON)+  , customContext      :: !(IORef context)+  }  -- | Initializes the context out of the environment-initialize :: Throws Error.Parsing => Throws Error.EnvironmentVariableNotSet => ApiInfo.Event -> IO Context-initialize ApiInfo.Event{..} = do+initialize+  :: Throws Error.Parsing+  => Throws Error.EnvironmentVariableNotSet+  => IORef context+  -> ApiInfo.Event+  -> IO (Context context)+initialize customContextRef ApiInfo.Event{..} = do   functionName          <- Environment.functionName   version               <- Environment.functionVersion   logStream             <- Environment.logStreamName   logGroup              <- Environment.logGroupName   memoryLimitInMb       <- Environment.functionMemory+   Environment.setXRayTrace traceId   pure Context     { functionName       = functionName@@ -43,4 +49,5 @@     , xrayTraceId        = traceId     , awsRequestId       = awsRequestId     , deadline           = deadlineMs+    , customContext      = customContextRef     }
src/Aws/Lambda/Runtime/Publish.hs view
@@ -9,7 +9,7 @@  import Control.Monad (void) import Data.Aeson-import qualified Data.ByteString.Char8 as ByteString+import qualified Data.Text.Encoding as T import qualified Network.HTTP.Client as Http  import qualified Aws.Lambda.Runtime.API.Endpoints as Endpoints@@ -18,14 +18,14 @@ import qualified Aws.Lambda.Runtime.Error as Error  -- | Publishes the result back to AWS Lambda-result :: LambdaResult -> String -> Context -> Http.Manager -> IO ()+result :: LambdaResult -> String -> Context context -> Http.Manager -> IO () result lambdaResult lambdaApi context manager = do   let Endpoints.Endpoint endpoint = Endpoints.response lambdaApi (awsRequestId context)   rawRequest <- Http.parseRequest endpoint    let requestBody = case lambdaResult of-        (StandaloneLambdaResult res) -> Http.RequestBodyBS (ByteString.pack res)-        (ApiGatewayResult res) -> Http.RequestBodyLBS (encode res)+        (StandaloneLambdaResult res) -> Http.RequestBodyBS (T.encodeUtf8 . unLambdaResponseBody $ res)+        (ApiGatewayResult res)       -> Http.RequestBodyLBS (encode res)       request = rawRequest                 { Http.method = "POST"                 , Http.requestBody = requestBody@@ -34,22 +34,22 @@   void $ Http.httpNoBody request manager  -- | Publishes an invocation error back to AWS Lambda-invocationError :: Error.Invocation -> String -> Context -> Http.Manager -> IO ()+invocationError :: Error.Invocation -> String -> Context context -> Http.Manager -> IO () invocationError err lambdaApi context =   publish err (Endpoints.invocationError lambdaApi $ awsRequestId context) context  -- | Publishes a parsing error back to AWS Lambda-parsingError :: Error.Parsing -> String -> Context -> Http.Manager -> IO ()+parsingError :: Error.Parsing -> String -> Context context -> Http.Manager -> IO () parsingError err lambdaApi context =   publish err (Endpoints.invocationError lambdaApi $ awsRequestId context)     context  -- | Publishes a runtime initialization error back to AWS Lambda-runtimeInitError :: ToJSON err => err -> String -> Context -> Http.Manager -> IO ()+runtimeInitError :: ToJSON err => err -> String -> Context context -> Http.Manager -> IO () runtimeInitError err lambdaApi =   publish err (Endpoints.runtimeInitError lambdaApi) -publish :: ToJSON err => err -> Endpoints.Endpoint -> Context -> Http.Manager -> IO ()+publish :: ToJSON err => err -> Endpoints.Endpoint -> Context context -> Http.Manager -> IO () publish err (Endpoints.Endpoint endpoint) Context{..} manager = do   rawRequest <- Http.parseRequest endpoint 
+ src/Aws/Lambda/Utilities.hs view
@@ -0,0 +1,9 @@+module Aws.Lambda.Utilities (toJSONText) where++import Data.Aeson+import qualified Data.ByteString.Lazy.Char8 as LazyByteString+import Data.Text+import qualified Data.Text as T++toJSONText :: ToJSON a => a -> Text+toJSONText = T.pack . LazyByteString.unpack . encode