hal 0.4.9 → 0.4.10
raw patch · 16 files changed
+538/−28 lines, 16 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ AWS.Lambda.Events.EventBridge: EventBridgeEvent :: Text -> Text -> Text -> Text -> Text -> UTCTime -> Text -> [Text] -> a -> EventBridgeEvent' a
+ AWS.Lambda.Events.EventBridge: [$sel:account:EventBridgeEvent] :: EventBridgeEvent' a -> Text
+ AWS.Lambda.Events.EventBridge: [$sel:detail:EventBridgeEvent] :: EventBridgeEvent' a -> a
+ AWS.Lambda.Events.EventBridge: [$sel:detailType:EventBridgeEvent] :: EventBridgeEvent' a -> Text
+ AWS.Lambda.Events.EventBridge: [$sel:id:EventBridgeEvent] :: EventBridgeEvent' a -> Text
+ AWS.Lambda.Events.EventBridge: [$sel:region:EventBridgeEvent] :: EventBridgeEvent' a -> Text
+ AWS.Lambda.Events.EventBridge: [$sel:resources:EventBridgeEvent] :: EventBridgeEvent' a -> [Text]
+ AWS.Lambda.Events.EventBridge: [$sel:source:EventBridgeEvent] :: EventBridgeEvent' a -> Text
+ AWS.Lambda.Events.EventBridge: [$sel:time:EventBridgeEvent] :: EventBridgeEvent' a -> UTCTime
+ AWS.Lambda.Events.EventBridge: [$sel:version:EventBridgeEvent] :: EventBridgeEvent' a -> Text
+ AWS.Lambda.Events.EventBridge: data EventBridgeEvent' a
+ AWS.Lambda.Events.EventBridge: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (AWS.Lambda.Events.EventBridge.EventBridgeEvent' a)
+ AWS.Lambda.Events.EventBridge: instance Data.Aeson.Types.ToJSON.ToJSON a => Data.Aeson.Types.ToJSON.ToJSON (AWS.Lambda.Events.EventBridge.EventBridgeEvent' a)
+ AWS.Lambda.Events.EventBridge: instance Data.Foldable.Foldable AWS.Lambda.Events.EventBridge.EventBridgeEvent'
+ AWS.Lambda.Events.EventBridge: instance Data.Traversable.Traversable AWS.Lambda.Events.EventBridge.EventBridgeEvent'
+ AWS.Lambda.Events.EventBridge: instance GHC.Base.Functor AWS.Lambda.Events.EventBridge.EventBridgeEvent'
+ AWS.Lambda.Events.EventBridge: instance GHC.Classes.Eq a => GHC.Classes.Eq (AWS.Lambda.Events.EventBridge.EventBridgeEvent' a)
+ AWS.Lambda.Events.EventBridge: instance GHC.Generics.Generic (AWS.Lambda.Events.EventBridge.EventBridgeEvent' a)
+ AWS.Lambda.Events.EventBridge: instance GHC.Show.Show a => GHC.Show.Show (AWS.Lambda.Events.EventBridge.EventBridgeEvent' a)
+ AWS.Lambda.Events.EventBridge: type EventBridgeEvent = EventBridgeEvent' Value
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: Operation :: Text -> Operation
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: ParameterStoreChange :: Operation -> Text -> Type -> Maybe Text -> ParameterStoreChange
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: SecureString :: Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: String :: Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: StringList :: Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: [$sel:description:ParameterStoreChange] :: ParameterStoreChange -> Maybe Text
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: [$sel:name:ParameterStoreChange] :: ParameterStoreChange -> Text
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: [$sel:operation:ParameterStoreChange] :: ParameterStoreChange -> Operation
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: [$sel:type_:ParameterStoreChange] :: ParameterStoreChange -> Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: data ParameterStoreChange
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: data Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance Data.Aeson.Types.FromJSON.FromJSON AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Operation
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance Data.Aeson.Types.FromJSON.FromJSON AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.ParameterStoreChange
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance Data.Aeson.Types.FromJSON.FromJSON AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance Data.Aeson.Types.ToJSON.ToJSON AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Operation
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance Data.Aeson.Types.ToJSON.ToJSON AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.ParameterStoreChange
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance Data.Aeson.Types.ToJSON.ToJSON AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Classes.Eq AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Operation
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Classes.Eq AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.ParameterStoreChange
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Classes.Eq AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Classes.Ord AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Enum.Bounded AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Enum.Enum AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Generics.Generic AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Operation
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Generics.Generic AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.ParameterStoreChange
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Generics.Generic AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Show.Show AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Operation
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Show.Show AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.ParameterStoreChange
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: instance GHC.Show.Show AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Type
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: newtype Operation
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: pattern Create :: Operation
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: pattern Delete :: Operation
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: pattern LabelParameterVersion :: Operation
+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange: pattern Update :: Operation
Files
- CHANGELOG.md +12/−0
- hal.cabal +8/−3
- src/AWS/Lambda/Events/ApiGateway/ProxyResponse.hs +3/−0
- src/AWS/Lambda/Events/EventBridge.hs +146/−0
- src/AWS/Lambda/Events/EventBridge/Detail/SSM/ParameterStoreChange.hs +134/−0
- src/AWS/Lambda/Events/Kafka.hs +2/−0
- src/AWS/Lambda/RuntimeClient.hs +19/−4
- src/AWS/Lambda/RuntimeClient/Internal.hs +3/−0
- test/AWS/Lambda/Events/ApiGateway/ProxyRequest/Gen/Parameters.hs +3/−0
- test/AWS/Lambda/Events/ApiGateway/ProxyRequest/Gen/Resource.hs +3/−0
- test/AWS/Lambda/Events/EventBridge/Detail/SSM/ParameterStoreChange/Gen.hs +24/−0
- test/AWS/Lambda/Events/EventBridge/Gen.hs +69/−0
- test/AWS/Lambda/Events/EventBridge/Spec.hs +80/−0
- test/AWS/Lambda/Events/Kafka/Gen.hs +3/−0
- test/Gen/Header.hs +3/−0
- test/Spec.hs +26/−21
CHANGELOG.md view
@@ -5,6 +5,18 @@ The format is based on [Keep a Changelog][chg] and this project adheres to [Haskell's Package Versioning Policy][pvp] +## `0.4.10` - 2022-03-22++ - Add `AWS.Lambda.Events.EventBridge.EventBridgeEvent` type for+ subscribing Lambda functions to AWS EventBridge Events+ - Add `AWS.Lambda.Events.EventBridge.SSM.ParameterStoreChange` type+ for parsing AWS Systems Manager Parameter Store Change events+ delivered via AWS EventBridge.+ - When the runtime encounters an error, write it out to the Cloudwatch logs+ (via stderr), and do so in a format that can be guaranteed and later+ extended.+ - Eliminate redundant import compiler warnings+ ## `0.4.9` - 2022-02-28 - Add `KafkaEvent` type for subscribing Lambda functions to Kafka
hal.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.33.0.+-- This file has been generated from package.yaml by hpack version 0.34.5. -- -- see: https://github.com/sol/hpack ----- hash: 804d66701a670f6c063313f96b6b5c9754cf0a4a67b7fc4651e11ed8ab059608+-- hash: 7e0538a9f4a9a866214c8f9649acec329a1eb823391fd64f645a21b85459f566 name: hal-version: 0.4.9+version: 0.4.10 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@@ -35,6 +35,8 @@ AWS.Lambda.Context AWS.Lambda.Events.ApiGateway.ProxyRequest AWS.Lambda.Events.ApiGateway.ProxyResponse+ AWS.Lambda.Events.EventBridge+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange AWS.Lambda.Events.Kafka AWS.Lambda.Events.S3 AWS.Lambda.Events.SQS@@ -98,6 +100,9 @@ AWS.Lambda.Events.ApiGateway.ProxyRequest.Spec AWS.Lambda.Events.ApiGateway.ProxyResponse.Gen AWS.Lambda.Events.ApiGateway.ProxyResponse.Spec+ AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Gen+ AWS.Lambda.Events.EventBridge.Gen+ AWS.Lambda.Events.EventBridge.Spec AWS.Lambda.Events.Kafka.Gen AWS.Lambda.Events.Kafka.Spec Gen.Header
src/AWS/Lambda/Events/ApiGateway/ProxyResponse.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-| Module : AWS.Lambda.Events.ApiGateway.ProxyResponse Description : Data types that represent typical lambda responses@@ -35,7 +36,9 @@ import Data.Hashable (Hashable) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as H+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup ((<>))+#endif import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified Data.Text.Lazy as TL
+ src/AWS/Lambda/Events/EventBridge.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE NamedFieldPuns #-}++{-|+Module : AWS.Lambda.Events.EventBridge+Description : Data types for consuming EventBridge events.+License : BSD3+Stability : stable++Data types for Lambda functions which subscribe to AWS EventBridge+events.++-}++module AWS.Lambda.Events.EventBridge (+ EventBridgeEvent,+ EventBridgeEvent'(..)+) where++import Control.Monad (unless)+import Data.Aeson+import Data.Text (Text)+import Data.Time (UTCTime)+import GHC.Generics (Generic)+import Prelude hiding (id)++-- | Convenience alias for events of unknown type. Most of the time+-- you will want to define or use a data type @Foo@ representing the+-- @detail@ payload, and write an @instance FromJSON Foo@ to get+-- @instance FromJSON ('EventBridgeEvent'' Foo)@. See the+-- @AWS.Lambda.Events.EventBridge.Detail.*@ modules for data types+-- which you can use.+type EventBridgeEvent = EventBridgeEvent' Value++-- | Represents an event from Amazon EventBridge.+--+-- See the <https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-events.html AWS documentation>+-- for information about events, and a sample payload.+data EventBridgeEvent' a = EventBridgeEvent {+ -- | By default, this is set to 0 (zero) in all events.+ version :: Text,++ -- | A Version 4 UUID that's generated for every event. You can use+ -- id to trace events as they move through rules to targets.+ id :: Text,++ -- | Identifies, in combination with the source field, the fields+ -- and values that appear in the detail field.+ --+ -- Events that are delivered by CloudTrail have AWS API Call via+ -- CloudTrail as the value for detail-type.+ --+ -- __NOTE:__ This is called @detail-type@ in the AWS payload.+ detailType :: Text,++ -- | Identifies the service that generated the event. All events+ -- that come from AWS services begin with "aws." Customer-generated+ -- events can have any value here, as long as it doesn't begin with+ -- "aws." We recommend the use of Java package-name style reverse+ -- domain-name strings.+ --+ -- To find the correct value for source for an AWS service, see The+ -- condition keys table, select a service from the list, and look+ -- for the service prefix. For example, the source value for Amazon+ -- CloudFront is aws.cloudfront.+ source :: Text,++ -- | The 12-digit number identifying an AWS account.+ account :: Text,++ -- | The event timestamp, which can be specified by the service+ -- originating the event. If the event spans a time interval, the+ -- service can report the start time, so this value might be before+ -- the time the event is received.+ time :: UTCTime,++ -- | Identifies the AWS Region where the event originated.+ region :: Text,++ -- | A JSON array that contains ARNs that identify resources that+ -- are involved in the event. The service generating the event+ -- determines whether to include these ARNs. For example, Amazon EC2+ -- instance state-changes include Amazon EC2 instance ARNs, Auto+ -- Scaling events include ARNs for both instances and Auto Scaling+ -- groups, but API calls with AWS CloudTrail do not include resource+ -- ARNs.+ resources :: [Text],++ -- | A JSON object that contains information about the event. The+ -- service generating the event determines the content of this+ -- field. The detail content can be as simple as two fields. AWS API+ -- call events have detail objects with approximately 50 fields+ -- nested several levels deep.+ detail :: a+} deriving (Eq, Show, Generic, Functor, Foldable, Traversable)++instance FromJSON a => FromJSON (EventBridgeEvent' a) where+ parseJSON = withObject "EventBridgeEvent" $ \o -> do+ version <- o .: "version"+ unless (version == "0") $ fail "version != 0"+ id <- o .: "id"+ detailType <- o .: "detail-type"+ source <- o .: "source"+ account <- o .: "account"+ time <- o .: "time"+ region <- o .: "region"+ resources <- o .: "resources"+ detail <- o .: "detail"++ pure EventBridgeEvent+ { version+ , id+ , detailType+ , source+ , account+ , time+ , region+ , resources+ , detail+ }++instance ToJSON a => ToJSON (EventBridgeEvent' a) where+ toJSON event = object+ [ "version" .= version event+ , "id" .= id event+ , "detail-type" .= detailType event+ , "source" .= source event+ , "account" .= account event+ , "time" .= time event+ , "region" .= region event+ , "resources" .= resources event+ , "detail" .= detail event+ ]++ toEncoding event = pairs $ mconcat+ [ "version" .= version event+ , "id" .= id event+ , "detail-type" .= detailType event+ , "source" .= source event+ , "account" .= account event+ , "time" .= time event+ , "region" .= region event+ , "resources" .= resources event+ , "detail" .= detail event+ ]
+ src/AWS/Lambda/Events/EventBridge/Detail/SSM/ParameterStoreChange.hs view
@@ -0,0 +1,134 @@+{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE NamedFieldPuns #-}++-- |+-- Module : AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange+-- Description : Data types for AWS Systems Manager Parameter Store Change events.+-- License : BSD3+-- Stability : stable+module AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange+ ( ParameterStoreChange (..),+ Operation (.., Create, Update, Delete, LabelParameterVersion),+ Type (..),+ )+where++import Data.Aeson+ ( FromJSON (..),+ ToJSON (..),+ object,+ pairs,+ withObject,+ withText,+ (.:),+ (.:?),+ (.=),+ )+import qualified Data.Aeson as Aeson+import Data.Aeson.Encoding (text)+import Data.Text (Text)+import GHC.Generics (Generic)++-- | A @Parameter Store Change@ event from Amazon EventBridge. This+-- structure corresponds to the contents of the @"detail"@ field of an+-- EventBridge event, so a full payload from EventBridge can be parsed+-- into a @'AWS.Lambda.Events.EventBridge'' ParameterStoreChange@.+--+-- Sample event payloads are provided in the+-- <https://docs.aws.amazon.com/systems-manager/latest/userguide/monitoring-systems-manager-event-examples.html#SSM-Parameter-Store-event-types AWS Systems Manager User Guide>.+data ParameterStoreChange = ParameterStoreChange+ { operation :: Operation,+ name :: Text,+ type_ :: Type,+ description :: Maybe Text+ }+ deriving (Eq, Show, Generic)++instance FromJSON ParameterStoreChange where+ parseJSON = withObject "ParameterStoreChange" $ \o -> do+ operation <- o .: "operation"+ name <- o .: "name"+ type_ <- o .: "type"+ description <- o .:? "description"++ pure+ ParameterStoreChange+ { operation,+ name,+ type_,+ description+ }++instance ToJSON ParameterStoreChange where+ toJSON change =+ object+ [ "operation" .= operation change,+ "name" .= name change,+ "type" .= type_ change,+ "description" .= description change+ ]++ toEncoding change =+ pairs $+ mconcat+ [ "operation" .= operation change,+ "name" .= name change,+ "type" .= type_ change,+ "description" .= description change+ ]++-- | AWS provides no schema for the @"operation"@ field, so we provide+-- a newtype wrapper and pattern synonyms which we think are complete,+-- based on AWS documentation.+newtype Operation = Operation Text deriving (Eq, Show, Generic)++pattern Create :: Operation+pattern Create = Operation "Create"++pattern Update :: Operation+pattern Update = Operation "Update"++pattern Delete :: Operation+pattern Delete = Operation "Delete"++pattern LabelParameterVersion :: Operation+pattern LabelParameterVersion = Operation "LabelParameterVersion"++{-# COMPLETE Create, Update, Delete, LabelParameterVersion #-}++instance FromJSON Operation where+ parseJSON = withText "Operation" $ \case+ "Create" -> pure Create+ "Update" -> pure Update+ "Delete" -> pure Delete+ "LabelParameterVersion" -> pure LabelParameterVersion+ t -> fail $ "Unrecognized operation: " ++ show t++instance ToJSON Operation where+ toJSON (Operation op) = Aeson.String op+ toEncoding (Operation op) = text op++-- | AWS provides no schema for the @"type"@ field, but these are the+-- only three types of parameters you can create in Parameter Store.+data Type = String | StringList | SecureString+ deriving (Eq, Ord, Show, Enum, Bounded, Generic)++instance FromJSON Type where+ parseJSON = withText "Type" $ \case+ "String" -> pure String+ "StringList" -> pure StringList+ "SecureString" -> pure SecureString+ t -> fail $ "Unrecognised type: " ++ show t++instance ToJSON Type where+ toJSON =+ Aeson.String . \case+ String -> "String"+ StringList -> "StringList"+ SecureString -> "SecureString"++ toEncoding =+ text . \case+ String -> "String"+ StringList -> "StringList"+ SecureString -> "SecureString"
src/AWS/Lambda/Events/Kafka.hs view
@@ -53,7 +53,9 @@ import Data.Map (Map) import Data.Maybe (catMaybes, maybeToList) import Data.Scientific (toBoundedInteger)+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup ((<>))+#endif import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Encoding as TE
src/AWS/Lambda/RuntimeClient.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-| Module : AWS.Lambda.RuntimeClient Description : HTTP related machinery for talking to the AWS Lambda Custom Runtime interface.@@ -19,11 +20,10 @@ import AWS.Lambda.Context (LambdaContext) import AWS.Lambda.Internal (StaticContext, getStaticContext) import AWS.Lambda.RuntimeClient.Internal (eventResponseToNextData)-import Control.Applicative ((<*>)) import Control.Concurrent (threadDelay) import Control.Exception (IOException, displayException, throw, try)-import Control.Monad (unless)+import Control.Monad (unless, void) import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Aeson (Value, encode) import Data.Aeson.Parser (value')@@ -34,7 +34,9 @@ import Data.Conduit (ConduitM, runConduit, yield, (.|)) import Data.Conduit.Attoparsec (sinkParser)+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup ((<>))+#endif import GHC.Generics (Generic (..)) import Network.HTTP.Client (BodyReader, HttpException, Manager, Request,@@ -59,6 +61,7 @@ status413, statusIsSuccessful) import System.Environment (getEnv)+import System.IO (hPutStrLn, stderr) -- | Lambda runtime error that we pass back to AWS data LambdaError = LambdaError@@ -153,8 +156,10 @@ Right () -> return () sendEventError :: RuntimeClientConfig -> BS.ByteString -> String -> IO ()-sendEventError (RuntimeClientConfig baseRuntimeRequest manager _) reqId e =- fmap (const ()) $ runtimeClientRetry $ flip httpNoBody manager $ toEventErrorRequest reqId e baseRuntimeRequest+sendEventError (RuntimeClientConfig baseRuntimeRequest manager _) reqId e = do+ logErrorMsg e+ let request = httpNoBody (toEventErrorRequest reqId e baseRuntimeRequest) manager+ void $ runtimeClientRetry request sendInitError :: Request -> Manager -> String -> IO () sendInitError baseRuntimeRequest manager e =@@ -260,3 +265,13 @@ setRequestPath :: BS.ByteString -> Request -> Request setRequestPath p req = req { path = p }++-- Log Helpers++-- TODO: This logging more-or-less looks like other runtimes, but there doesn't+-- seem to be any specific standard or recommendations around this, and it+-- varies across runtimes. In the future, it may make sense to enable user+-- specific formatting (similar to the Rust runtime). But for now, not sure+-- we'll ever see such an ask.+logErrorMsg :: String -> IO ()+logErrorMsg = hPutStrLn stderr . (<>) "ERROR Message: " . show
src/AWS/Lambda/RuntimeClient/Internal.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-| Module : AWS.Lambda.RuntimeClient.Internal Description : Internal HTTP related machinery for talking to the AWS Lambda Custom Runtime interface.@@ -22,7 +23,9 @@ import qualified Data.ByteString.Internal as BSI import qualified Data.ByteString.Lazy as BSW import Data.CaseInsensitive (original)+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup ((<>))+#endif import Data.Text.Encoding (decodeUtf8) import Data.Time.Clock.POSIX (posixSecondsToUTCTime) import Network.HTTP.Client (Response, responseBody,
test/AWS/Lambda/Events/ApiGateway/ProxyRequest/Gen/Parameters.hs view
@@ -1,9 +1,12 @@+{-# LANGUAGE CPP #-} module AWS.Lambda.Events.ApiGateway.ProxyRequest.Gen.Parameters where import Control.Monad.Trans.State import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as H+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup ((<>))+#endif import Data.Set (Set) import qualified Data.Set as S import Data.Text (Text)
test/AWS/Lambda/Events/ApiGateway/ProxyRequest/Gen/Resource.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE CPP #-} module AWS.Lambda.Events.ApiGateway.ProxyRequest.Gen.Resource where @@ -9,7 +10,9 @@ import qualified Data.HashMap.Strict as H import Data.List.NonEmpty (NonEmpty) import qualified Data.List.NonEmpty as NE+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup ((<>))+#endif import qualified Data.Set as S import Data.Text (Text) import qualified Data.Text as T
+ test/AWS/Lambda/Events/EventBridge/Detail/SSM/ParameterStoreChange/Gen.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE NamedFieldPuns #-}++module AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Gen where++import AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange (Operation(..), ParameterStoreChange(..))+import Hedgehog (Gen)+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range+import Prelude hiding (id)++parameterStoreChange :: Gen ParameterStoreChange+parameterStoreChange = do+ operation <- Gen.element [Create, Update, Delete, LabelParameterVersion]+ name <- Gen.text (Range.linear 1 200) Gen.unicode+ type_ <- Gen.enumBounded+ description <- Gen.maybe $ Gen.text (Range.linear 1 200) Gen.unicode++ pure ParameterStoreChange+ { operation+ , name+ , type_+ , description+ }
+ test/AWS/Lambda/Events/EventBridge/Gen.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE ApplicativeDo #-}+{-# LANGUAGE NamedFieldPuns #-}++module AWS.Lambda.Events.EventBridge.Gen where++import AWS.Lambda.Events.EventBridge (EventBridgeEvent'(..),+ EventBridgeEvent)+import Data.List (intersperse)+import Data.Text (Text)+import qualified Data.Text as T+import Data.Time (UTCTime)+import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import Data.Traversable (for)+import Hedgehog (Gen)+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range+import Prelude hiding (id)++eventBridgeEvent :: Gen EventBridgeEvent+eventBridgeEvent = do+ let version = "0"+ id <- uuid+ detailType <- Gen.text (Range.linear 1 200) Gen.unicode+ source <- Gen.text (Range.linear 1 200) Gen.unicode+ account <- awsAccount+ time <- utcTime+ region <- awsRegion+ resources <- Gen.list (Range.linear 1 5) resource+ let detail = "{}"++ pure EventBridgeEvent+ { version+ , id+ , detailType+ , source+ , account+ , time+ , region+ , resources+ , detail+ }++uuid :: Gen Text+uuid = do+ uuidParts <- for [ 7, 4, 4, 4, 12, 2 ] $ \n ->+ Gen.text (Range.singleton n) Gen.hexit+ pure $ T.intercalate "-" uuidParts++utcTime :: Gen UTCTime+utcTime = do+ int <- Gen.int64+ (Range.exponentialFrom 1600000000000 1650000000000 1700000000000)+ let (seconds, millis) = int `divMod` 1000+ posixSeconds = fromIntegral seconds + fromIntegral millis / 1000+ pure $ posixSecondsToUTCTime posixSeconds++awsAccount :: Gen Text+awsAccount = Gen.text (Range.singleton 12) Gen.digit++awsRegion :: Gen Text+awsRegion = Gen.element ["ap-south-1", "us-east-2", "eu-west-3"]++resource :: Gen Text+resource = do+ acc <- awsAccount+ svc <- Gen.text (Range.linear 1 10) Gen.lower+ reg <- awsRegion+ res <- Gen.text (Range.linear 1 20) Gen.alphaNum+ pure $ T.intercalate ":" ["arn:aws", svc, reg, acc, res]
+ test/AWS/Lambda/Events/EventBridge/Spec.hs view
@@ -0,0 +1,80 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE QuasiQuotes #-}++module AWS.Lambda.Events.EventBridge.Spec where++import Prelude hiding (id)++import AWS.Lambda.Events.EventBridge+import qualified AWS.Lambda.Events.EventBridge.Gen as EventBridge+import qualified AWS.Lambda.Events.EventBridge.Detail.SSM.ParameterStoreChange.Gen as ParameterStoreChange+import Data.Aeson (decode, eitherDecode, encode,+ object, (.=), Value(..))+import Data.ByteString.Lazy (ByteString)+import Data.Time (UTCTime(..), fromGregorian,+ secondsToDiffTime)+import Hedgehog (forAll, tripping)+import Test.Hspec (Spec, describe, shouldBe, specify)+import Test.Hspec.Hedgehog (hedgehog)+import Test.Hspec.Runner (hspec)+import Text.RawString.QQ (r)++spec :: Spec+spec = do+ describe "EventBridge" $ do+ specify "read sample payload" $+ eitherDecode samplePayload `shouldBe` Right expectedPayload++ describe "properties" $ do+ specify "EventBridgeEvent tripping" $+ hedgehog $ do+ payload <- forAll EventBridge.eventBridgeEvent+ tripping payload encode decode++ describe "SSM" $+ specify "ParameterStoreChange tripping" $+ hedgehog $ do+ change <- forAll ParameterStoreChange.parameterStoreChange+ tripping change encode decode++samplePayload :: ByteString+samplePayload = [r|+{+ "version":"0",+ "id":"01234567-0123-0123-0123-0123456789ab",+ "detail-type":"Maintenance Window Target Registration Notification",+ "source":"aws.ssm",+ "account":"123456789012",+ "time":"2016-11-16T00:58:37Z",+ "region":"us-east-2",+ "resources":[+ "arn:aws:ssm:us-east-2:123456789012:maintenancewindow/mw-0ed7251d3fcf6e0c2",+ "arn:aws:ssm:us-east-2:123456789012:windowtarget/e7265f13-3cc5-4f2f-97a9-7d3ca86c32a6"+ ],+ "detail":{+ "window-target-id":"e7265f13-3cc5-4f2f-97a9-7d3ca86c32a6",+ "window-id":"mw-0ed7251d3fcf6e0c2",+ "status":"REGISTERED"+ }+}+|]++expectedPayload :: EventBridgeEvent+expectedPayload = EventBridgeEvent+ { version = "0"+ , id = "01234567-0123-0123-0123-0123456789ab"+ , detailType = "Maintenance Window Target Registration Notification"+ , source = "aws.ssm"+ , account = "123456789012"+ , time = UTCTime (fromGregorian 2016 11 16) (secondsToDiffTime $ 58 * 60 + 37)+ , region = "us-east-2"+ , resources =+ [ "arn:aws:ssm:us-east-2:123456789012:maintenancewindow/mw-0ed7251d3fcf6e0c2"+ , "arn:aws:ssm:us-east-2:123456789012:windowtarget/e7265f13-3cc5-4f2f-97a9-7d3ca86c32a6"+ ]+ , detail = object+ [ "window-target-id" .= String "e7265f13-3cc5-4f2f-97a9-7d3ca86c32a6"+ , "window-id" .= String "mw-0ed7251d3fcf6e0c2"+ , "status" .= String "REGISTERED"+ ]+ }
test/AWS/Lambda/Events/Kafka/Gen.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} module AWS.Lambda.Events.Kafka.Gen where import AWS.Lambda.Events.Kafka (KafkaEvent(KafkaEvent),@@ -14,7 +15,9 @@ import qualified Data.List.NonEmpty as NE import Data.List.NonEmpty (NonEmpty) import Data.Map (Map)+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup ((<>))+#endif import Data.Text (Text) import qualified Data.Text as T import Data.Time (UTCTime)
test/Gen/Header.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} module Gen.Header where import Control.Monad.Trans.State@@ -6,7 +7,9 @@ import Data.Char (toUpper, toLower) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as H+#if !MIN_VERSION_base(4,11,0) import Data.Semigroup ((<>))+#endif import Data.Set (Set) import qualified Data.Set as S import Data.Text (Text)
test/Spec.hs view
@@ -1,28 +1,32 @@-import AWS.Lambda.Context (ClientApplication (..),- ClientContext (..),- CognitoIdentity (..),- LambdaContext (..))-import qualified Gen.Header as Header+{-# LANGUAGE CPP #-}+import AWS.Lambda.Context (ClientApplication (..),+ ClientContext (..),+ CognitoIdentity (..),+ LambdaContext (..))+import qualified Gen.Header as Header import qualified AWS.Lambda.Events.ApiGateway.ProxyRequest.Spec as ProxyRequest import qualified AWS.Lambda.Events.ApiGateway.ProxyResponse.Spec as ProxyResponse -import qualified AWS.Lambda.Events.Kafka.Spec as Kafka-import AWS.Lambda.Internal (StaticContext (..))-import AWS.Lambda.RuntimeClient.Internal (eventResponseToNextData)-import Data.Aeson (Value (Null))-import qualified Data.CaseInsensitive as CI-import Data.Map (singleton)-import Data.Semigroup ((<>))-import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import qualified AWS.Lambda.Events.EventBridge.Spec as EventBridge+import qualified AWS.Lambda.Events.Kafka.Spec as Kafka+import AWS.Lambda.Internal (StaticContext (..))+import AWS.Lambda.RuntimeClient.Internal (eventResponseToNextData)+import Data.Aeson (Value (Null))+import qualified Data.CaseInsensitive as CI+import Data.Map (singleton)+#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup ((<>))+#endif+import Data.Time.Clock.POSIX (posixSecondsToUTCTime) import Hedgehog-import qualified Hedgehog.Gen as Gen-import qualified Hedgehog.Range as Range-import Network.HTTP.Client.Internal (Response (..))-import Network.HTTP.Types (Header)-import Test.Hspec (describe, it, shouldBe,- shouldStartWith, specify)-import Test.Hspec.Hedgehog (hedgehog)-import Test.Hspec.Runner (hspec)+import qualified Hedgehog.Gen as Gen+import qualified Hedgehog.Range as Range+import Network.HTTP.Client.Internal (Response (..))+import Network.HTTP.Types (Header)+import Test.Hspec (describe, it, shouldBe,+ shouldStartWith, specify)+import Test.Hspec.Hedgehog (hedgehog)+import Test.Hspec.Runner (hspec) main :: IO () main =@@ -31,6 +35,7 @@ describe "ApiGateway" $ do describe "ProxyRequest" ProxyRequest.spec describe "ProxyResponse" ProxyResponse.spec+ describe "EventBridge" EventBridge.spec describe "Kafka" Kafka.spec describe "Event Response Data" $ do