packages feed

hal 0.4.5 → 0.4.6

raw patch · 5 files changed

+500/−97 lines, 5 filesdep +base64-bytestringdep +case-insensitivedep +unordered-containersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: base64-bytestring, case-insensitive, unordered-containers

API changes (from Hackage documentation)

- AWS.Lambda.Context: [appPackageName] :: ClientApplication -> Text
- AWS.Lambda.Context: [appTitle] :: ClientApplication -> Text
- AWS.Lambda.Context: [appVersionCode] :: ClientApplication -> Text
- AWS.Lambda.Context: [appVersionName] :: ClientApplication -> Text
- AWS.Lambda.Context: [awsRequestId] :: LambdaContext -> Text
- AWS.Lambda.Context: [clientContext] :: LambdaContext -> Maybe ClientContext
- AWS.Lambda.Context: [client] :: ClientContext -> ClientApplication
- AWS.Lambda.Context: [custom] :: ClientContext -> Map Text Text
- AWS.Lambda.Context: [deadline] :: LambdaContext -> UTCTime
- AWS.Lambda.Context: [environment] :: ClientContext -> Map Text Text
- AWS.Lambda.Context: [functionMemorySize] :: LambdaContext -> Int
- AWS.Lambda.Context: [functionName] :: LambdaContext -> Text
- AWS.Lambda.Context: [functionVersion] :: LambdaContext -> Text
- AWS.Lambda.Context: [identityId] :: CognitoIdentity -> Text
- AWS.Lambda.Context: [identityPoolId] :: CognitoIdentity -> Text
- AWS.Lambda.Context: [identity] :: LambdaContext -> Maybe CognitoIdentity
- AWS.Lambda.Context: [invokedFunctionArn] :: LambdaContext -> Text
- AWS.Lambda.Context: [logGroupName] :: LambdaContext -> Text
- AWS.Lambda.Context: [logStreamName] :: LambdaContext -> Text
- AWS.Lambda.Context: [xRayTraceId] :: LambdaContext -> Text
- AWS.Lambda.Events.SQS: [approximateFirstReceiveTimestamp] :: Attributes -> Text
- AWS.Lambda.Events.SQS: [approximateReceiveCount] :: Attributes -> Text
- AWS.Lambda.Events.SQS: [attributes] :: SQSEvent -> Attributes
- AWS.Lambda.Events.SQS: [awsRegion] :: SQSEvent -> Text
- AWS.Lambda.Events.SQS: [body] :: SQSEvent -> Text
- AWS.Lambda.Events.SQS: [eventSourceARN] :: SQSEvent -> Text
- AWS.Lambda.Events.SQS: [eventSource] :: SQSEvent -> Text
- AWS.Lambda.Events.SQS: [md5OfBody] :: SQSEvent -> Text
- AWS.Lambda.Events.SQS: [messageAttributes] :: SQSEvent -> Map Text Text
- AWS.Lambda.Events.SQS: [messageId] :: SQSEvent -> Text
- AWS.Lambda.Events.SQS: [receiptHandle] :: SQSEvent -> Text
- AWS.Lambda.Events.SQS: [records] :: Records -> [SQSEvent]
- AWS.Lambda.Events.SQS: [senderId] :: Attributes -> Text
- AWS.Lambda.Events.SQS: [sentTimestamp] :: Attributes -> Text
- AWS.Lambda.Internal: [awsRequestId] :: DynamicContext -> Text
- AWS.Lambda.Internal: [clientContext] :: DynamicContext -> Maybe ClientContext
- AWS.Lambda.Internal: [deadline] :: DynamicContext -> UTCTime
- AWS.Lambda.Internal: [functionMemorySize] :: StaticContext -> Int
- AWS.Lambda.Internal: [functionName] :: StaticContext -> Text
- AWS.Lambda.Internal: [functionVersion] :: StaticContext -> Text
- AWS.Lambda.Internal: [identity] :: DynamicContext -> Maybe CognitoIdentity
- AWS.Lambda.Internal: [invokedFunctionArn] :: DynamicContext -> Text
- AWS.Lambda.Internal: [logGroupName] :: StaticContext -> Text
- AWS.Lambda.Internal: [logStreamName] :: StaticContext -> Text
- AWS.Lambda.Internal: [xRayTraceId] :: DynamicContext -> Text
- AWS.Lambda.RuntimeClient: sendInitError :: RuntimeClientConfig -> String -> IO ()
+ AWS.Lambda.Context: [$sel:appPackageName:ClientApplication] :: ClientApplication -> Text
+ AWS.Lambda.Context: [$sel:appTitle:ClientApplication] :: ClientApplication -> Text
+ AWS.Lambda.Context: [$sel:appVersionCode:ClientApplication] :: ClientApplication -> Text
+ AWS.Lambda.Context: [$sel:appVersionName:ClientApplication] :: ClientApplication -> Text
+ AWS.Lambda.Context: [$sel:awsRequestId:LambdaContext] :: LambdaContext -> Text
+ AWS.Lambda.Context: [$sel:client:ClientContext] :: ClientContext -> ClientApplication
+ AWS.Lambda.Context: [$sel:clientContext:LambdaContext] :: LambdaContext -> Maybe ClientContext
+ AWS.Lambda.Context: [$sel:custom:ClientContext] :: ClientContext -> Map Text Text
+ AWS.Lambda.Context: [$sel:deadline:LambdaContext] :: LambdaContext -> UTCTime
+ AWS.Lambda.Context: [$sel:environment:ClientContext] :: ClientContext -> Map Text Text
+ AWS.Lambda.Context: [$sel:functionMemorySize:LambdaContext] :: LambdaContext -> Int
+ AWS.Lambda.Context: [$sel:functionName:LambdaContext] :: LambdaContext -> Text
+ AWS.Lambda.Context: [$sel:functionVersion:LambdaContext] :: LambdaContext -> Text
+ AWS.Lambda.Context: [$sel:identity:LambdaContext] :: LambdaContext -> Maybe CognitoIdentity
+ AWS.Lambda.Context: [$sel:identityId:CognitoIdentity] :: CognitoIdentity -> Text
+ AWS.Lambda.Context: [$sel:identityPoolId:CognitoIdentity] :: CognitoIdentity -> Text
+ AWS.Lambda.Context: [$sel:invokedFunctionArn:LambdaContext] :: LambdaContext -> Text
+ AWS.Lambda.Context: [$sel:logGroupName:LambdaContext] :: LambdaContext -> Text
+ AWS.Lambda.Context: [$sel:logStreamName:LambdaContext] :: LambdaContext -> Text
+ AWS.Lambda.Context: [$sel:xRayTraceId:LambdaContext] :: LambdaContext -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: Identity :: Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Identity
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: ProxyRequest :: Text -> HashMap (CI Text) Text -> HashMap (CI Text) [Text] -> HashMap Text Text -> HashMap Text Text -> RequestContext a -> Text -> Text -> HashMap Text Text -> HashMap Text [Text] -> ByteString -> ProxyRequest a
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: RequestContext :: Text -> Text -> Maybe a -> Text -> Text -> Maybe Text -> Text -> Identity -> Maybe Text -> Text -> Text -> Maybe Text -> Text -> RequestContext a
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:accessKey:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:accountId:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:accountId:RequestContext] :: RequestContext a -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:apiId:RequestContext] :: RequestContext a -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:apiKey:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:apiKeyId:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:authorizer:RequestContext] :: RequestContext a -> Maybe a
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:body:ProxyRequest] :: ProxyRequest a -> ByteString
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:caller:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:cognitoAuthenticationProvider:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:cognitoAuthenticationType:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:cognitoIdentityId:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:cognitoIdentityPoolId:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:domainName:RequestContext] :: RequestContext a -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:domainPrefix:RequestContext] :: RequestContext a -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:extendedRequestId:RequestContext] :: RequestContext a -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:headers:ProxyRequest] :: ProxyRequest a -> HashMap (CI Text) Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:httpMethod:ProxyRequest] :: ProxyRequest a -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:httpMethod:RequestContext] :: RequestContext a -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:identity:RequestContext] :: RequestContext a -> Identity
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:multiValueHeaders:ProxyRequest] :: ProxyRequest a -> HashMap (CI Text) [Text]
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:multiValueQueryStringParameters:ProxyRequest] :: ProxyRequest a -> HashMap Text [Text]
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:path:ProxyRequest] :: ProxyRequest a -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:path:RequestContext] :: RequestContext a -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:pathParameters:ProxyRequest] :: ProxyRequest a -> HashMap Text Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:queryStringParameters:ProxyRequest] :: ProxyRequest a -> HashMap Text Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:requestContext:ProxyRequest] :: ProxyRequest a -> RequestContext a
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:requestId:RequestContext] :: RequestContext a -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:resource:ProxyRequest] :: ProxyRequest a -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:resourceId:RequestContext] :: RequestContext a -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:resourcePath:RequestContext] :: RequestContext a -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:sourceIp:Identity] :: Identity -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:stage:RequestContext] :: RequestContext a -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:stageVariables:ProxyRequest] :: ProxyRequest a -> HashMap Text Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:user:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:userAgent:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: [$sel:userArn:Identity] :: Identity -> Maybe Text
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: data Identity
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: data ProxyRequest a
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: data RequestContext a
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: instance Data.Aeson.Types.FromJSON.FromJSON AWS.Lambda.Events.ApiGateway.ProxyRequest.Identity
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (AWS.Lambda.Events.ApiGateway.ProxyRequest.ProxyRequest a)
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (AWS.Lambda.Events.ApiGateway.ProxyRequest.RequestContext a)
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: instance GHC.Generics.Generic (AWS.Lambda.Events.ApiGateway.ProxyRequest.ProxyRequest a)
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: instance GHC.Generics.Generic AWS.Lambda.Events.ApiGateway.ProxyRequest.Identity
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: type NoAuthorizer = Value
+ AWS.Lambda.Events.ApiGateway.ProxyRequest: type StrictlyNoAuthorizer = Void
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: ProxyBody :: Text -> Text -> Bool -> ProxyBody
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: ProxyResponse :: Status -> HashMap (CI Text) [Text] -> ProxyBody -> ProxyResponse
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: Status :: Int -> ByteString -> Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: [$sel:body:ProxyResponse] :: ProxyResponse -> ProxyBody
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: [$sel:contentType:ProxyBody] :: ProxyBody -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: [$sel:isBase64Encoded:ProxyBody] :: ProxyBody -> Bool
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: [$sel:multiValueHeaders:ProxyResponse] :: ProxyResponse -> HashMap (CI Text) [Text]
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: [$sel:serialized:ProxyBody] :: ProxyBody -> Text
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: [$sel:status:ProxyResponse] :: ProxyResponse -> Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: [statusCode] :: Status -> Int
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: [statusMessage] :: Status -> ByteString
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: accepted202 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: addHeader :: Text -> Text -> ProxyResponse -> ProxyResponse
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: applicationJson :: ToJSON a => a -> ProxyBody
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: badGateway502 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: badRequest400 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: conflict409 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: continue100 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: created201 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: data ProxyBody
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: data ProxyResponse
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: data Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: expectationFailed417 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: forbidden403 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: found302 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: gatewayTimeout504 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: genericBinary :: Text -> ByteString -> ProxyBody
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: gone410 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: httpVersionNotSupported505 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: imATeapot418 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: instance Data.Aeson.Types.ToJSON.ToJSON AWS.Lambda.Events.ApiGateway.ProxyResponse.ProxyResponse
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: instance GHC.Show.Show AWS.Lambda.Events.ApiGateway.ProxyResponse.ProxyBody
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: instance GHC.Show.Show AWS.Lambda.Events.ApiGateway.ProxyResponse.ProxyResponse
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: internalServerError500 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: lengthRequired411 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: methodNotAllowed405 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: movedPermanently301 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: multipleChoices300 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: networkAuthenticationRequired511 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: noContent204 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: nonAuthoritative203 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: notAcceptable406 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: notFound404 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: notImplemented501 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: notModified304 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: ok200 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: partialContent206 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: paymentRequired402 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: permanentRedirect308 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: preconditionFailed412 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: preconditionRequired428 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: proxyAuthenticationRequired407 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: requestEntityTooLarge413 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: requestHeaderFieldsTooLarge431 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: requestTimeout408 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: requestURITooLong414 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: requestedRangeNotSatisfiable416 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: resetContent205 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: response :: Status -> ProxyBody -> ProxyResponse
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: seeOther303 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: serviceUnavailable503 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: setHeader :: Text -> Text -> ProxyResponse -> ProxyResponse
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status100 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status101 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status200 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status201 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status202 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status203 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status204 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status205 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status206 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status300 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status301 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status302 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status303 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status304 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status305 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status307 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status308 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status400 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status401 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status402 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status403 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status404 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status405 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status406 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status407 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status408 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status409 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status410 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status411 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status412 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status413 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status414 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status415 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status416 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status417 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status418 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status422 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status426 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status428 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status429 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status431 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status500 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status501 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status502 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status503 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status504 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status505 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: status511 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: switchingProtocols101 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: temporaryRedirect307 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: textPlain :: Text -> ProxyBody
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: tooManyRequests429 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: unauthorized401 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: unprocessableEntity422 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: unsupportedMediaType415 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: upgradeRequired426 :: Status
+ AWS.Lambda.Events.ApiGateway.ProxyResponse: useProxy305 :: Status
+ AWS.Lambda.Events.SQS: [$sel:approximateFirstReceiveTimestamp:Attributes] :: Attributes -> Text
+ AWS.Lambda.Events.SQS: [$sel:approximateReceiveCount:Attributes] :: Attributes -> Text
+ AWS.Lambda.Events.SQS: [$sel:attributes:SQSEvent] :: SQSEvent -> Attributes
+ AWS.Lambda.Events.SQS: [$sel:awsRegion:SQSEvent] :: SQSEvent -> Text
+ AWS.Lambda.Events.SQS: [$sel:body:SQSEvent] :: SQSEvent -> Text
+ AWS.Lambda.Events.SQS: [$sel:eventSource:SQSEvent] :: SQSEvent -> Text
+ AWS.Lambda.Events.SQS: [$sel:eventSourceARN:SQSEvent] :: SQSEvent -> Text
+ AWS.Lambda.Events.SQS: [$sel:md5OfBody:SQSEvent] :: SQSEvent -> Text
+ AWS.Lambda.Events.SQS: [$sel:messageAttributes:SQSEvent] :: SQSEvent -> Map Text Text
+ AWS.Lambda.Events.SQS: [$sel:messageId:SQSEvent] :: SQSEvent -> Text
+ AWS.Lambda.Events.SQS: [$sel:receiptHandle:SQSEvent] :: SQSEvent -> Text
+ AWS.Lambda.Events.SQS: [$sel:records:Records] :: Records -> [SQSEvent]
+ AWS.Lambda.Events.SQS: [$sel:senderId:Attributes] :: Attributes -> Text
+ AWS.Lambda.Events.SQS: [$sel:sentTimestamp:Attributes] :: Attributes -> Text
+ AWS.Lambda.Internal: [$sel:awsRequestId:DynamicContext] :: DynamicContext -> Text
+ AWS.Lambda.Internal: [$sel:clientContext:DynamicContext] :: DynamicContext -> Maybe ClientContext
+ AWS.Lambda.Internal: [$sel:deadline:DynamicContext] :: DynamicContext -> UTCTime
+ AWS.Lambda.Internal: [$sel:functionMemorySize:StaticContext] :: StaticContext -> Int
+ AWS.Lambda.Internal: [$sel:functionName:StaticContext] :: StaticContext -> Text
+ AWS.Lambda.Internal: [$sel:functionVersion:StaticContext] :: StaticContext -> Text
+ AWS.Lambda.Internal: [$sel:identity:DynamicContext] :: DynamicContext -> Maybe CognitoIdentity
+ AWS.Lambda.Internal: [$sel:invokedFunctionArn:DynamicContext] :: DynamicContext -> Text
+ AWS.Lambda.Internal: [$sel:logGroupName:StaticContext] :: StaticContext -> Text
+ AWS.Lambda.Internal: [$sel:logStreamName:StaticContext] :: StaticContext -> Text
+ AWS.Lambda.Internal: [$sel:xRayTraceId:DynamicContext] :: DynamicContext -> Text
+ AWS.Lambda.RuntimeClient: getNextData :: RuntimeClientConfig -> IO (ByteString, Value, Either String LambdaContext)

Files

hal.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 2175dc8b8db69d64e82ae49c3130cc5d820d63bc28a956f8d4aa3af961c4ddb5+-- hash: d43ec18e49bb7ce77fd444cdf150662cb6ff0f3c8ba1349dbd4318e6b5bc7e29  name:           hal-version:        0.4.5+version:        0.4.6 synopsis:       A runtime environment for Haskell applications running on AWS Lambda. description:    This library uniquely supports different types of AWS Lambda Handlers for your                 needs/comfort with advanced Haskell. Instead of exposing a single function@@ -32,6 +32,8 @@   exposed-modules:       AWS.Lambda.Combinators       AWS.Lambda.Context+      AWS.Lambda.Events.ApiGateway.ProxyRequest+      AWS.Lambda.Events.ApiGateway.ProxyResponse       AWS.Lambda.Events.S3       AWS.Lambda.Events.SQS       AWS.Lambda.Internal@@ -42,12 +44,14 @@       Paths_hal   hs-source-dirs:       src-  default-extensions: OverloadedStrings BangPatterns DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveLift DeriveTraversable EmptyCase GeneralizedNewtypeDeriving InstanceSigs LambdaCase MultiParamTypeClasses MultiWayIf OverloadedStrings PatternSynonyms ScopedTypeVariables+  default-extensions: OverloadedStrings BangPatterns DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveLift DeriveTraversable DuplicateRecordFields EmptyCase GeneralizedNewtypeDeriving InstanceSigs LambdaCase MultiParamTypeClasses MultiWayIf OverloadedStrings PatternSynonyms ScopedTypeVariables   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -fno-warn-partial-type-signatures -fno-warn-name-shadowing -fwarn-tabs -fwarn-unused-imports -fwarn-missing-signatures -fwarn-incomplete-patterns   build-depends:       aeson     , base >=4.7 && <5+    , base64-bytestring     , bytestring+    , case-insensitive     , conduit     , conduit-extra     , containers@@ -59,4 +63,5 @@     , mtl     , text     , time+    , unordered-containers   default-language: Haskell2010
+ src/AWS/Lambda/Events/ApiGateway/ProxyRequest.hs view
@@ -0,0 +1,174 @@+{-|+Module      : AWS.Lambda.Events.ApiGateway.ProxyRequest+Description : Data types that represent typical lambda responses+Copyright   : (c) Nike, Inc., 2019+License     : BSD3+Maintainer  : nathan.fairhurst@nike.com, fernando.freire@nike.com+Stability   : stable++This module exposes types used to model incoming __proxy__ requests from AWS+API Gateway.  These types are a light pass over the incoming JSON+representation.+-}+module AWS.Lambda.Events.ApiGateway.ProxyRequest+    ( ProxyRequest(..)+    , RequestContext(..)+    , Identity(..)+    , NoAuthorizer+    , StrictlyNoAuthorizer+    ) where++import           Data.Aeson                  (FromJSON, Value, parseJSON,+                                              withObject, (.:), (.:?))+import           Data.ByteString.Base64.Lazy (decodeLenient)+import           Data.ByteString.Lazy        (ByteString)+import           Data.CaseInsensitive        (CI, mk)+import           Data.Foldable               (fold)+import           Data.HashMap.Strict         (HashMap, foldrWithKey, insert)+import           Data.Text                   (Text)+import qualified Data.Text.Lazy              as TL+import qualified Data.Text.Lazy.Encoding     as TLE+import           Data.Void                   (Void)+import           GHC.Generics                (Generic (..))++-- This function is available in Data.Functor as of base 4.11, but we define it+-- here for compatibility.+(<&>) :: Functor f => f a -> (a -> b) -> f b+(<&>) x f = f <$> x++infixl 1 <&>++data Identity = Identity+    { cognitoIdentityPoolId         :: Maybe Text+    , accountId                     :: Maybe Text+    , cognitoIdentityId             :: Maybe Text+    , caller                        :: Maybe Text+    , apiKey                        :: Maybe Text+    , sourceIp                      :: Text+    , accessKey                     :: Maybe Text+    , cognitoAuthenticationType     :: Maybe Text+    , cognitoAuthenticationProvider :: Maybe Text+    , userArn                       :: Maybe Text+    , apiKeyId                      :: Maybe Text+    , userAgent                     :: Maybe Text+    , user                          :: Maybe Text+    } deriving (Generic)++instance FromJSON Identity++data RequestContext a = RequestContext+    { path              :: Text+    , accountId         :: Text+    , authorizer        :: Maybe a+    , resourceId        :: Text+    , stage             :: Text+    , domainPrefix      :: Maybe Text+    , requestId         :: Text+    , identity          :: Identity+    , domainName        :: Maybe Text+    , resourcePath      :: Text+    , httpMethod        :: Text+    , extendedRequestId :: Maybe Text+    , apiId             :: Text+    }++instance FromJSON a => FromJSON (RequestContext a) where+    parseJSON = withObject "ProxyRequest" $ \v ->+        RequestContext <$> v .: "path" <*> v .: "accountId" <*>+        v .:? "authorizer" <*>+        v .: "resourceId" <*>+        v .: "stage" <*>+        v .:? "domainPrefix" <*>+        v .: "requestId" <*>+        v .: "identity" <*>+        v .:? "domainName" <*>+        v .: "resourcePath" <*>+        v .: "httpMethod" <*>+        v .:? "extendedRequestId" <*>+        v .: "apiId"++-- TODO: Should also include websocket fields+-- | This type is for representing events that come from API Gateway via the+-- Lambda Proxy integration (forwarding HTTP data directly, rather than a+-- custom integration).  It will automatically decode the event that comes in.+--+-- The 'ProxyRequest' notably has one parameter for the type of information+-- returned by the API Gateway's custom authorizer (if applicable).  This type+-- must also implement FromJSON so that it can be decoded.  If you do not+-- expect this data to be populated we recommended using the 'NoAuthorizer'+-- type exported from this module (which is just an alias for 'Value').  If+-- there _must not_ be authorizer populated (this is unlikely) then use the+-- 'StrictlyNoAuthorizer' type.+--+-- @+--     {-\# LANGUAGE NamedFieldPuns \#-}+--     {-\# LANGUAGE DuplicateRecordFields \#-}+--+--     module Main where+--+--     import AWS.Lambda.Runtime (pureRuntime)+--     import AWS.Lambda.Events.ApiGateway.ProxyRequest (ProxyRequest(..), NoAuthorizer)+--     import AWS.Lambda.Events.ApiGateway.ProxyResponse (ProxyResponse(..), textPlain, forbidden403, ok200)+--+--     myHandler :: ProxyRequest NoAuthorizer -> ProxyResponse+--     myHandler ProxyRequest { httpMethod = \"GET\", path = "/say_hello" } =+--         ProxyResponse+--         {   status = ok200+--         ,   body = textPlain \"Hello\"+--         ,   headers = mempty+--         ,   multiValueHeaders = mempty+--         }+--     myHandler _ =+--         ProxyResponse+--         {   status = forbidden403+--         ,   body = textPlain \"Forbidden\"+--         ,   headers = mempty+--         ,   multiValueHeaders = mempty+--         }+--+--     main :: IO ()+--     main = pureRuntime myHandler+-- @+data ProxyRequest a = ProxyRequest+    { path                            :: Text+    , headers                         :: HashMap (CI Text) Text+    , multiValueHeaders               :: HashMap (CI Text) [Text]+    , pathParameters                  :: HashMap Text Text+    , stageVariables                  :: HashMap Text Text+    , requestContext                  :: RequestContext a+    , resource                        :: Text+    , httpMethod                      :: Text+    , queryStringParameters           :: HashMap Text Text+    , multiValueQueryStringParameters :: HashMap Text [Text]+    , body                            :: ByteString+    } deriving (Generic)++toCIHashMap :: HashMap Text a -> HashMap (CI Text) a+toCIHashMap = foldrWithKey (insert . mk) mempty++toByteString :: Bool -> TL.Text -> ByteString+toByteString isBase64Encoded =+    if isBase64Encoded+        then decodeLenient . TLE.encodeUtf8+        else TLE.encodeUtf8++-- | For ignoring API Gateway custom authorizer values+type NoAuthorizer = Value++-- | For ensuring that there were no API Gateway custom authorizer values (this+-- is not likely to be useful, you probably want 'NoAuthorizer')+type StrictlyNoAuthorizer = Void++instance FromJSON a => FromJSON (ProxyRequest a) where+    parseJSON = withObject "ProxyRequest" $ \v ->+        ProxyRequest <$> v .: "path" <*>+        (v .:? "headers" <&> toCIHashMap . fold) <*>+        (v .:? "multiValueHeaders" <&> toCIHashMap . fold) <*>+        (v .:? "pathParameters" <&> fold) <*>+        (v .:? "stageVariables" <&> fold) <*>+        v .: "requestContext" <*>+        v .: "resource" <*>+        v .: "httpMethod" <*>+        (v .:? "queryStringParameters" <&> fold) <*>+        (v .:? "multiValueQueryStringParameters" <&> fold) <*>+        (toByteString <$> v .: "isBase64Encoded" <*> (v .:? "body" <&> fold))
+ src/AWS/Lambda/Events/ApiGateway/ProxyResponse.hs view
@@ -0,0 +1,210 @@+{-|+Module      : AWS.Lambda.Events.ApiGateway.ProxyResponse+Description : Data types that represent typical lambda responses+Copyright   : (c) Nike, Inc., 2019+License     : BSD3+Maintainer  : nathan.fairhurst@nike.com, fernando.freire@nike.com+Stability   : stable++This module enable exposes the required types for responding to API Gateway+Proxy Events.  Responses must return a status, body, and optionaly headers.+Multiple smart contructors and helpers are provided to help encapsulated+details like header case-insensitivity, multiple header copies, correct base64+encoding, and default content type.+-}+module AWS.Lambda.Events.ApiGateway.ProxyResponse+    ( ProxyResponse(..)+    , response+    , addHeader+    , setHeader+    , ProxyBody(..)+    , textPlain+    , applicationJson+    , genericBinary+    , module Network.HTTP.Types.Status+    ) where++import           Data.Aeson                (ToJSON, encode, object, toJSON,+                                            (.=))+import           Data.ByteString           (ByteString)+import qualified Data.ByteString.Base64    as B64+import           Data.CaseInsensitive      (CI, mk, original)+import           Data.HashMap.Strict       (HashMap, foldrWithKey, insert,+                                            insertWith)+import           Data.Semigroup            ((<>))+import qualified Data.Text                 as T+import qualified Data.Text.Encoding        as TE+import qualified Data.Text.Lazy            as TL+import qualified Data.Text.Lazy.Encoding   as TLE+import           Network.HTTP.Types.Status (Status (..), accepted202,+                                            badGateway502, badRequest400,+                                            conflict409, continue100,+                                            created201, expectationFailed417,+                                            forbidden403, found302,+                                            gatewayTimeout504, gone410,+                                            httpVersionNotSupported505,+                                            imATeapot418,+                                            internalServerError500,+                                            lengthRequired411,+                                            methodNotAllowed405,+                                            movedPermanently301,+                                            multipleChoices300,+                                            networkAuthenticationRequired511,+                                            noContent204, nonAuthoritative203,+                                            notAcceptable406, notFound404,+                                            notImplemented501, notModified304,+                                            ok200, partialContent206,+                                            paymentRequired402,+                                            permanentRedirect308,+                                            preconditionFailed412,+                                            preconditionRequired428,+                                            proxyAuthenticationRequired407,+                                            requestEntityTooLarge413,+                                            requestHeaderFieldsTooLarge431,+                                            requestTimeout408,+                                            requestURITooLong414,+                                            requestedRangeNotSatisfiable416,+                                            resetContent205, seeOther303,+                                            serviceUnavailable503, status100,+                                            status101, status200, status201,+                                            status202, status203, status204,+                                            status205, status206, status300,+                                            status301, status302, status303,+                                            status304, status305, status307,+                                            status308, status400, status401,+                                            status402, status403, status404,+                                            status405, status406, status407,+                                            status408, status409, status410,+                                            status411, status412, status413,+                                            status414, status415, status416,+                                            status417, status418, status422,+                                            status426, status428, status429,+                                            status431, status500, status501,+                                            status502, status503, status504,+                                            status505, status511,+                                            switchingProtocols101,+                                            temporaryRedirect307,+                                            tooManyRequests429, unauthorized401,+                                            unprocessableEntity422,+                                            unsupportedMediaType415,+                                            upgradeRequired426, useProxy305)++-- | Type that represents the body returned to an API Gateway when using HTTP+-- Lambda Proxy integration.  It is highly recommended that you do not use this+-- type directly, and instead use the smart constructors exposed such as+-- 'textPlain', 'applicationJson', and 'genericBinary'.  These make sure that+-- the base64 encodings work transparently.+data ProxyBody = ProxyBody+    { contentType     :: T.Text+    , serialized      :: T.Text+    , isBase64Encoded :: Bool+    } deriving (Show)++-- | A response returned to an API Gateway when using the HTTP Lambda Proxy+-- integration.  ContentType will be set based on the ProxyBody (recommended)+-- if a value is not present in the headers field.+--+-- This type can be constructed explicity or via the smart constructor+-- `response`.  Headers can then be added incrementally with `addHeader` or+-- `setHeader`.  The smart constructor pattern is recommended because it avoids+-- some of the awkwardness of dealing with the multiValueHeaders field's type.+--+-- @+-- {-\# LANGUAGE NamedFieldPuns \#-}+-- {-\# LANGUAGE DuplicateRecordFields \#-}+-- {-\# LANGUAGE OverloadedStrings \#-}+--+-- module Main where+--+-- import AWS.Lambda.Runtime (pureRuntime)+-- import AWS.Lambda.Events.ApiGateway.ProxyRequest (ProxyRequest(..), NoAuthorizer)+-- import AWS.Lambda.Events.ApiGateway.ProxyResponse (ProxyResponse(..), textPlain, forbidden403, ok200, response)+--+-- myHandler :: ProxyRequest NoAuthorizer -> ProxyResponse+-- myHandler ProxyRequest { httpMethod = \"GET\", path = "/say_hello" } =+--     -- Smart Constructor and added header (recommended)+--     addHeader "My-Custom-Header" "Value" $+--       response ok200 $ textPlain \"Hello\"+-- myHandler _ =+--     -- Explicit Construction (not recommended)+--     ProxyResponse+--     {   status = forbidden403+--     ,   body = textPlain \"Forbidden\"+--     ,   multiValueHeaders =+--           fromList [(mk "My-Custom-Header", ["Other Value])]+--     }+--+-- main :: IO ()+-- main = pureRuntime myHandler+-- @+data ProxyResponse = ProxyResponse+    { status            :: Status+    , multiValueHeaders :: HashMap (CI T.Text) [T.Text]+    , body              :: ProxyBody+    } deriving (Show)++-- | Smart constructor for creating a ProxyResponse from a status and a body+response :: Status -> ProxyBody -> ProxyResponse+response =+  flip ProxyResponse mempty++-- | Add a header to the ProxyResponse.  If there was already a value for this+-- header, this one is __added__, meaning the response will include multiple+-- copies of this header (valid by the HTTP spec).  This does NOT replace any+-- previous headers or their values.+addHeader :: T.Text -> T.Text -> ProxyResponse -> ProxyResponse+addHeader header value (ProxyResponse s mvh b) =+  ProxyResponse s (insertWith (<>) (mk header) [value] mvh) b++-- | Set a header to the ProxyResponse.  If there were any previous values for+-- this header they are __all replaced__ by this new value.+setHeader :: T.Text -> T.Text -> ProxyResponse -> ProxyResponse+setHeader header value (ProxyResponse s mvh b) =+  ProxyResponse s (insert (mk header) [value] mvh) b++-- | Smart constructor for creating a ProxyBody with an arbitrary ByteString of+-- the chosen content type.  Use this smart constructor to avoid invalid JSON+-- representations of binary data.+--+-- From here it is easy to make more specific body constructors:+--+-- @+-- imageGif :: ByteString -> ProxyBody+-- imageGif = genericBinary "image/gif"+--+-- imageJpeg :: ByteString -> ProxyBody+-- imageJpeg = genericBinary "image/jpeg"+-- @+genericBinary :: T.Text -> ByteString -> ProxyBody+genericBinary contentType x =+    ProxyBody contentType (TE.decodeUtf8 $ B64.encode x) True++-- | Smart constructor for creating a simple body of text.+textPlain :: T.Text -> ProxyBody+textPlain x = ProxyBody "text/plain; charset=utf-8" x False++-- | Smart constructor for creating a simple body of JSON.+applicationJson :: ToJSON a => a -> ProxyBody+applicationJson x =+    ProxyBody+        "application/json; charset=utf-8"+        (TL.toStrict $ TLE.decodeUtf8 $ encode x)+        False++-- | Smart constructor for creating a simple body of a GIF (that has already+-- been converted to a ByteString).++instance ToJSON ProxyResponse where+    toJSON (ProxyResponse status mvh (ProxyBody contentType body isBase64Encoded)) =+        let unCI = foldrWithKey (insert . original) mempty+        in object+               [ "statusCode" .= statusCode status+               , "multiValueHeaders" .=+                     insertWith+                         (\_ old -> old)+                         ("Content-Type" :: T.Text)+                         [contentType]+                         (unCI mvh)+               , "body" .= body+               , "isBase64Encoded" .= isBase64Encoded+               ]
src/AWS/Lambda/Runtime/Value.hs view
@@ -39,98 +39,27 @@ ) where  import           AWS.Lambda.RuntimeClient (RuntimeClientConfig, getRuntimeClientConfig,-                                           getNextEvent, sendEventError, sendEventSuccess,-                                           sendInitError)+                                           getNextData, sendEventError, sendEventSuccess) import           AWS.Lambda.Combinators   (withIOInterface,                                            withFallibleInterface,                                            withPureInterface,                                            withoutContext) import           AWS.Lambda.Context       (LambdaContext(..), HasLambdaContext(..), runReaderTLambdaContext)-import           AWS.Lambda.Internal      (StaticContext, DynamicContext(DynamicContext),-                                           mkContext)-import           Control.Applicative      ((<*>)) import           Control.Exception        (SomeException, displayException) import           Control.Monad            (forever) import           Control.Monad.Catch      (MonadCatch, try) import           Control.Monad.IO.Class   (MonadIO, liftIO) import           Control.Monad.Reader     (MonadReader, ReaderT, local)-import           Data.Aeson               (FromJSON, FromJSON, ToJSON, decode, Value)+import           Data.Aeson               (ToJSON, Value) import           Data.Bifunctor           (first)-import qualified Data.ByteString.Char8    as BSC-import qualified Data.ByteString.Lazy     as BSW-import qualified Data.ByteString.Internal as BSI import           Data.Text                (unpack)-import           Data.Text.Encoding       (decodeUtf8)-import           Data.Time.Clock.POSIX    (posixSecondsToUTCTime)-import           Network.HTTP.Simple      (getResponseBody, getResponseHeader) import           System.Environment       (setEnv)-import           System.Envy              (decodeEnv) -exactlyOneHeader :: [a] -> Maybe a-exactlyOneHeader [a] = Just a-exactlyOneHeader _ = Nothing--maybeToEither :: b -> Maybe a -> Either b a-maybeToEither b ma = case ma of-  Nothing -> Left b-  Just a -> Right a---- Note: Does not allow whitespace-readMaybe :: (Read a) => String -> Maybe a-readMaybe s = case reads s of-  [(x,"")] -> Just x-  _ -> Nothing---- TODO: There must be a better way to do this-decodeHeaderValue :: FromJSON a => BSC.ByteString -> Maybe a-decodeHeaderValue = decode . BSW.pack . fmap BSI.c2w . BSC.unpack---- An empty array means we successfully decoded, but nothing was there--- If we have exactly one element, our outer maybe signals successful decode,---   and our inner maybe signals that there was content sent--- If we had more than one header value, the event was invalid-decodeOptionalHeader :: FromJSON a => [BSC.ByteString] -> Maybe (Maybe a)-decodeOptionalHeader header =-  case header of-    [] -> Just Nothing-    [x] -> fmap Just $ decodeHeaderValue x-    _ -> Nothing---runtimeLoop :: (HasLambdaContext r, MonadReader r m, MonadCatch m, MonadIO m, ToJSON result) => RuntimeClientConfig -> StaticContext ->+runtimeLoop :: (HasLambdaContext r, MonadReader r m, MonadCatch m, MonadIO m, ToJSON result) => RuntimeClientConfig ->   (Value -> m result) -> m ()-runtimeLoop runtimeClientConfig staticContext fn = do+runtimeLoop runtimeClientConfig fn = do   -- Get an event-  nextRes <- liftIO $ getNextEvent runtimeClientConfig--  -- If we got an event but our requestId is invalid/missing, there's no hope of meaningful recovery-  let reqIdBS = head $ getResponseHeader "Lambda-Runtime-Aws-Request-Id" nextRes--  let mTraceId = fmap decodeUtf8 $ exactlyOneHeader $ getResponseHeader "Lambda-Runtime-Trace-Id" nextRes-  let mFunctionArn = fmap decodeUtf8 $ exactlyOneHeader $ getResponseHeader "Lambda-Runtime-Invoked-Function-Arn" nextRes-  let mDeadline = do-        header <- exactlyOneHeader (getResponseHeader "Lambda-Runtime-Deadline-Ms" nextRes)-        milliseconds :: Double <- readMaybe $ BSC.unpack header-        return $ posixSecondsToUTCTime $ realToFrac $ milliseconds / 1000--  let mClientContext = decodeOptionalHeader $ getResponseHeader "Lambda-Runtime-Client-Context" nextRes-  let mIdentity = decodeOptionalHeader $ getResponseHeader "Lambda-Runtime-Cognito-Identity" nextRes--  -- Populate the context with values from headers-  let eCtx =-        -- combine our StaticContext and possible DynamicContext into a LambdaContext-        fmap (mkContext staticContext)-        -- convert the Maybe DynamicContext into an Either String DynamicContext-        $ maybeToEither "Runtime Error: Unable to decode Context from event response."-        -- Build the Dynamic Context, collapsing individual Maybes into a single Maybe-        $ DynamicContext (decodeUtf8 reqIdBS)-        <$> mFunctionArn-        <*> mTraceId-        <*> mDeadline-        <*> mClientContext-        <*> mIdentity--  let event = getResponseBody nextRes+  (reqIdBS, event, eCtx) <- liftIO $ getNextData runtimeClientConfig    result <- case eCtx of     Left e -> return $ Left e@@ -198,15 +127,10 @@ mRuntimeWithContext :: (HasLambdaContext r, MonadCatch m, MonadReader r m, MonadIO m, ToJSON result) =>   (Value -> m result) -> m () mRuntimeWithContext fn = do-  -- TODO: Hide the implementation details of StaticContext within-  -- RuntimeClientConfig that encapsulates more details   runtimeClientConfig <- liftIO getRuntimeClientConfig -  possibleStaticCtx <- liftIO $ (decodeEnv :: IO (Either String StaticContext))+  forever $ runtimeLoop runtimeClientConfig fn -  case possibleStaticCtx of-    Left err -> liftIO $ sendInitError runtimeClientConfig err-    Right staticContext -> forever $ runtimeLoop runtimeClientConfig staticContext fn  -- | For functions that can read the lambda context and use IO within the same monad. --
src/AWS/Lambda/RuntimeClient.hs view
@@ -10,23 +10,32 @@ module AWS.Lambda.RuntimeClient (   RuntimeClientConfig,   getRuntimeClientConfig,+  getNextData,   getNextEvent,   sendEventSuccess,   sendEventError,-  sendInitError ) where +import           AWS.Lambda.Context        (LambdaContext(..))+import           AWS.Lambda.Internal       (DynamicContext(..), StaticContext,+                                            mkContext)+import           Control.Applicative       ((<*>)) import           Control.Concurrent        (threadDelay) import           Control.Exception         (displayException, try, throw) import           Control.Monad             (unless) import           Control.Monad.IO.Class    (MonadIO, liftIO)-import           Data.Aeson                (encode, Value)+import           Data.Aeson                (decode, encode, Value) import           Data.Aeson.Parser         (value')-import           Data.Aeson.Types          (ToJSON)+import           Data.Aeson.Types          (FromJSON, ToJSON) import           Data.Bifunctor            (first) import qualified Data.ByteString           as BS+import qualified Data.ByteString.Char8     as BSC+import qualified Data.ByteString.Lazy      as BSW+import qualified Data.ByteString.Internal  as BSI import           Data.Conduit              (ConduitM, runConduit, yield, (.|)) import           Data.Conduit.Attoparsec   (sinkParser)+import           Data.Text.Encoding        (decodeUtf8)+import           Data.Time.Clock.POSIX     (posixSecondsToUTCTime) import           GHC.Generics              (Generic (..)) import           Network.HTTP.Client       (BodyReader, HttpException, Manager,                                             Request, Response, brRead,@@ -37,7 +46,9 @@                                             managerSetProxy, newManager,                                             noProxy, parseRequest, responseBody,                                             responseTimeoutNone, withResponse)-import           Network.HTTP.Simple       (getResponseStatus,+import           Network.HTTP.Simple       (getResponseBody,+                                            getResponseHeader,+                                            getResponseStatus,                                             setRequestBodyJSON,                                             setRequestBodyLBS,                                             setRequestCheckStatus,@@ -45,6 +56,7 @@                                             setRequestPath) import           Network.HTTP.Types.Status (status403, status413, statusIsSuccessful) import           System.Environment        (getEnv)+import           System.Envy               (decodeEnv)  -- | Lambda runtime error that we pass back to AWS data LambdaError = LambdaError@@ -55,7 +67,7 @@  instance ToJSON LambdaError -data RuntimeClientConfig = RuntimeClientConfig Request Manager+data RuntimeClientConfig = RuntimeClientConfig Request Manager StaticContext  -- Exposed Handlers @@ -80,12 +92,56 @@              , managerConnCount = 1              , managerIdleConnectionCount = 1              }-  return $ RuntimeClientConfig req man +  possibleStaticCtx <- liftIO $ (decodeEnv :: IO (Either String StaticContext))++  case possibleStaticCtx of+    Left err -> do+      liftIO $ sendInitError req man err+      error err+    Right staticCtx -> return $ RuntimeClientConfig req man staticCtx+++getNextData :: RuntimeClientConfig -> IO (BS.ByteString, Value, Either String LambdaContext)+getNextData runtimeClientConfig@(RuntimeClientConfig _ _ staticContext) = do+  nextRes <- getNextEvent runtimeClientConfig++  -- If we got an event but our requestId is invalid/missing, there's no hope of meaningful recovery+  let reqIdBS = head $ getResponseHeader "Lambda-Runtime-Aws-Request-Id" nextRes++  let mTraceId = fmap decodeUtf8 $ exactlyOneHeader $ getResponseHeader "Lambda-Runtime-Trace-Id" nextRes+  let mFunctionArn = fmap decodeUtf8 $ exactlyOneHeader $ getResponseHeader "Lambda-Runtime-Invoked-Function-Arn" nextRes+  let mDeadline = do+        header <- exactlyOneHeader (getResponseHeader "Lambda-Runtime-Deadline-Ms" nextRes)+        milliseconds :: Double <- readMaybe $ BSC.unpack header+        return $ posixSecondsToUTCTime $ realToFrac $ milliseconds / 1000++  let mClientContext = decodeOptionalHeader $ getResponseHeader "Lambda-Runtime-Client-Context" nextRes+  let mIdentity = decodeOptionalHeader $ getResponseHeader "Lambda-Runtime-Cognito-Identity" nextRes++  -- Build out the Dynamic portion of the Lambda Context+  let eDynCtx =+        maybeToEither "Runtime Error: Unable to decode Context from event response."+        -- Build the Dynamic Context, collapsing individual Maybes into a single Maybe+        $ DynamicContext (decodeUtf8 reqIdBS)+        <$> mFunctionArn+        <*> mTraceId+        <*> mDeadline+        <*> mClientContext+        <*> mIdentity++  -- combine our StaticContext and possible DynamicContext into a LambdaContext+  let eCtx = fmap (mkContext staticContext) eDynCtx++  let event = getResponseBody nextRes++  -- Return the interesting components+  return (reqIdBS, event, eCtx)+ -- AWS lambda guarantees that we will get valid JSON, -- so parsing is guaranteed to succeed. getNextEvent :: RuntimeClientConfig -> IO (Response Value)-getNextEvent rcc@(RuntimeClientConfig baseRuntimeRequest manager) = do+getNextEvent (RuntimeClientConfig baseRuntimeRequest manager _) = do   resOrEx <- runtimeClientRetryTry $ flip httpValue manager $ toNextEventRequest baseRuntimeRequest   let checkStatus res = if not $ statusIsSuccessful $ getResponseStatus res then         Left "Unexpected Runtime Error:  Could not retrieve next event."@@ -94,12 +150,12 @@   let resOrMsg = first (displayException :: HttpException -> String) resOrEx >>= checkStatus   case resOrMsg of     Left msg -> do-      _ <- sendInitError rcc msg+      _ <- sendInitError baseRuntimeRequest manager msg       error msg     Right y -> return y  sendEventSuccess :: ToJSON a => RuntimeClientConfig -> BS.ByteString -> a -> IO ()-sendEventSuccess rcc@(RuntimeClientConfig baseRuntimeRequest manager) reqId json = do+sendEventSuccess rcc@(RuntimeClientConfig baseRuntimeRequest manager _) reqId json = do   resOrEx <- runtimeClientRetryTry $ flip httpNoBody manager $ toEventSuccessRequest reqId json baseRuntimeRequest    let resOrTypedMsg = case resOrEx of@@ -126,12 +182,46 @@     Right () -> return ()  sendEventError :: RuntimeClientConfig -> BS.ByteString -> String -> IO ()-sendEventError (RuntimeClientConfig baseRuntimeRequest manager) reqId e =+sendEventError (RuntimeClientConfig baseRuntimeRequest manager _) reqId e =   fmap (const ()) $ runtimeClientRetry $ flip httpNoBody manager $ toEventErrorRequest reqId e baseRuntimeRequest -sendInitError :: RuntimeClientConfig -> String -> IO ()-sendInitError (RuntimeClientConfig baseRuntimeRequest manager) e =+sendInitError :: Request -> Manager -> String -> IO ()+sendInitError baseRuntimeRequest manager e =   fmap (const ()) $ runtimeClientRetry $ flip httpNoBody manager $ toInitErrorRequest e baseRuntimeRequest+++-- Helpers (mostly) for Headers++exactlyOneHeader :: [a] -> Maybe a+exactlyOneHeader [a] = Just a+exactlyOneHeader _ = Nothing++maybeToEither :: b -> Maybe a -> Either b a+maybeToEither b ma = case ma of+  Nothing -> Left b+  Just a -> Right a++-- Note: Does not allow whitespace+readMaybe :: (Read a) => String -> Maybe a+readMaybe s = case reads s of+  [(x,"")] -> Just x+  _ -> Nothing++-- TODO: There must be a better way to do this+decodeHeaderValue :: FromJSON a => BSC.ByteString -> Maybe a+decodeHeaderValue = decode . BSW.pack . fmap BSI.c2w . BSC.unpack++-- An empty array means we successfully decoded, but nothing was there+-- If we have exactly one element, our outer maybe signals successful decode,+--   and our inner maybe signals that there was content sent+-- If we had more than one header value, the event was invalid+decodeOptionalHeader :: FromJSON a => [BSC.ByteString] -> Maybe (Maybe a)+decodeOptionalHeader header =+  case header of+    [] -> Just Nothing+    [x] -> fmap Just $ decodeHeaderValue x+    _ -> Nothing+  -- Helpers for Requests with JSON Bodies