packages feed

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 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