packages feed

serverless-haskell 0.9.4 → 0.10.0

raw patch · 7 files changed

+315/−18 lines, 7 files

Files

serverless-haskell.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 1e05bad89b0aca82487e2433016e2d39a21f182bf384caab21fb8e56b3a5f2f6+-- hash: a98a36c8e69422845bcf7370c281cb9b0bbaac07f75b4e660c170bc782ced20c  name:           serverless-haskell-version:        0.9.4+version:        0.10.0 synopsis:       Deploying Haskell code onto AWS Lambda using Serverless description:    Utilities to help process the events from AWS Lambda when deployed with the serverless-haskell plugin. category:       AWS, Cloud, Network@@ -30,9 +30,11 @@       AWSLambda.Events       AWSLambda.Events.APIGateway       AWSLambda.Events.KinesisEvent+      AWSLambda.Events.MessageAttribute       AWSLambda.Events.Records       AWSLambda.Events.S3Event       AWSLambda.Events.SNSEvent+      AWSLambda.Events.SQSEvent       AWSLambda.Handler       AWSLambda.Orphans       Data.Aeson.Alternative@@ -72,6 +74,7 @@       AWSLambda.Events.KinesisEventSpec       AWSLambda.Events.S3EventSpec       AWSLambda.Events.SNSEventSpec+      AWSLambda.Events.SQSEventSpec       Data.Aeson.AlternativeSpec       Data.Aeson.EmbeddedSpec       Data.Aeson.TestUtil
src/AWSLambda/Events.hs view
@@ -11,6 +11,7 @@ import           AWSLambda.Events.KinesisEvent import           AWSLambda.Events.S3Event import           AWSLambda.Events.SNSEvent+import           AWSLambda.Events.SQSEvent  -- | Not yet implemented data DynamoDBEvent@@ -60,11 +61,12 @@ -- | Sum type for all possible Lambda events. -- Parameterised on the type of SNS Events to be handled. -- See @SNSEvent@ for details.-data LambdaEvent snsMessage+data LambdaEvent message   = S3 !S3Event   | DynamoDB !DynamoDBEvent   | KinesisStream !KinesisEvent-  | SNS !(SNSEvent snsMessage)+  | SNS !(SNSEvent message)+  | SQS !(SQSEvent message)   | SES !SESEvent   | Cognito !CognitoEvent   | CloudFormation !CloudFormationEvent@@ -84,10 +86,10 @@ -- | Attempt to parse the various event types. -- Any valid JSON that can't be parsed as a specific -- event type will result in a 'Custom' value.-instance FromText snsMessage =>-         FromJSON (LambdaEvent snsMessage) where+instance FromText message =>+         FromJSON (LambdaEvent message) where   parseJSON v =-    try S3 v <|> try KinesisStream v <|> try SNS v <|> pure (Custom v)+    try S3 v <|> try KinesisStream v <|> try SNS v <|> try SQS v <|> pure (Custom v)     where       try f = fmap f . parseJSON 
+ src/AWSLambda/Events/MessageAttribute.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}++{-|+Module: AWSLambda.Events.MessageAttribute+Description: Types for SQS and SNS message attributes+-}+module AWSLambda.Events.MessageAttribute where++import           Control.Lens             (makeLenses)+import           Data.Aeson.Casing        (aesonPrefix, pascalCase)+import           Data.Aeson.TH            (deriveFromJSON)+import           Data.Text                (Text)++data MessageAttribute = MessageAttribute+  { _maType  :: !Text+  , _maValue :: !Text+  } deriving (Eq, Show)++$(deriveFromJSON (aesonPrefix pascalCase) ''MessageAttribute)+$(makeLenses ''MessageAttribute)
src/AWSLambda/Events/SNSEvent.hs view
@@ -11,12 +11,13 @@ -} module AWSLambda.Events.SNSEvent where +import           Control.Applicative      ((<|>)) import           Control.Lens-import           Data.Aeson               (FromJSON (..), genericParseJSON)+import           Data.Aeson               (FromJSON (..), genericParseJSON,+                                           withObject, (.:), (.:?), (.!=)) import           Data.Aeson.Casing        (aesonDrop, pascalCase) import           Data.Aeson.Embedded import           Data.Aeson.TextValue-import           Data.Aeson.TH            (deriveFromJSON) import           Data.ByteString          (ByteString) import           Data.HashMap.Strict      (HashMap) import           Data.Text                (Text)@@ -25,16 +26,9 @@ import           Network.AWS.Data.Base64 import           Network.AWS.Data.Text    (FromText) +import           AWSLambda.Events.MessageAttribute import           AWSLambda.Events.Records -data MessageAttribute = MessageAttribute-  { _maType  :: !Text-  , _maValue :: !Text-  } deriving (Eq, Show)--$(deriveFromJSON (aesonDrop 3 pascalCase) ''MessageAttribute)-$(makeLenses ''MessageAttribute)- data SNSMessage message = SNSMessage   { _smMessage           :: !(TextValue message )   , _smMessageAttributes :: !(HashMap Text MessageAttribute)@@ -49,8 +43,28 @@   , _smUnsubscribeUrl    :: !Text   } deriving (Eq, Show, Generic) +-- When a lambda is triggered directly off of an SNS topic,+-- the SNS message contains message attributes and the URI+-- fields are cased as `SigningCertUrl` and `UnsubscribeUrl`.+-- When an SNS message is embedded in an SQS event,+-- the SNS message changes in two ways; `MessageAttributes`+-- is not present and the casing for the URI fields becomes+-- `SigningCertURL` and `UnsubscribeURL`.+-- For these reasons we must hand-roll the `FromJSON` instance. instance FromText message => FromJSON (SNSMessage message) where-  parseJSON = genericParseJSON $ aesonDrop 3 pascalCase+  parseJSON = withObject "SNSMessage'" $ \o ->+    SNSMessage+      <$> o .: "Message"+      <*> o .:? "MessageAttributes" .!= mempty+      <*> o .: "MessageId"+      <*> o .: "Signature"+      <*> o .: "SignatureVersion"+      <*> do o .: "SigningCertUrl" <|> o .: "SigningCertURL"+      <*> o .: "Subject"+      <*> o .: "Timestamp"+      <*> o .: "TopicArn"+      <*> o .: "Type"+      <*> do o .: "UnsubscribeUrl" <|> o .: "UnsubscribeURL"  $(makeLenses ''SNSMessage) 
+ src/AWSLambda/Events/SQSEvent.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards   #-}+{-# LANGUAGE TemplateHaskell   #-}++{-|+Module: AWSLambda.Events.SQSEvent+Description: Types for SQS Lambda events+-}+module AWSLambda.Events.SQSEvent where++import           Control.Lens+import           Data.Aeson               (FromJSON (..), genericParseJSON)+import           Data.Aeson.Casing        (aesonPrefix, camelCase)+import           Data.Aeson.Embedded+import           Data.Aeson.TextValue+import           Data.ByteString          (ByteString)+import           Data.HashMap.Strict      (HashMap)+import           Data.Text                (Text)+import           GHC.Generics             (Generic)+import           Network.AWS.Data.Base64+import           Network.AWS.Data.Text    (FromText)+import qualified Network.AWS.Types        as AWS++import           AWSLambda.Events.MessageAttribute+import           AWSLambda.Events.Records++data SQSMessage body = SQSMessage+  { _sqsmMessageId         :: !Text+  , _sqsmReceiptHandle     :: !Text+  , _sqsmBody              :: !(TextValue body)+  , _sqsmAttributes        :: !(HashMap Text Text)+  , _sqsmMessageAttributes :: !(HashMap Text MessageAttribute)+  , _sqsmMd5OfBody         :: !Text+  , _sqsmEventSource       :: !Text+  , _sqsmEventSourceARN    :: !Text+  , _sqsmAwsRegion         :: !AWS.Region+  } deriving (Show, Eq, Generic)++instance FromText message => FromJSON (SQSMessage message) where+  parseJSON = genericParseJSON $ aesonPrefix camelCase++$(makeLenses ''SQSMessage)++type SQSEvent body = RecordsEvent (SQSMessage body)++-- | A Traversal to get messages from an SQSEvent+sqsMessages :: Traversal (SQSEvent message) (SQSEvent message') message message'+sqsMessages = reRecords . traverse . sqsmBody . unTextValue++-- | A Traversal to get embedded JSON values from an SQSEvent+sqsEmbedded :: Traversal (SQSEvent (Embedded v)) (SQSEvent (Embedded v')) v v'+sqsEmbedded = sqsMessages . unEmbed++sqsBinary :: Traversal' (SQSEvent Base64) ByteString+sqsBinary = sqsMessages . _Base64
test/AWSLambda/Events/SNSEventSpec.hs view
@@ -3,6 +3,7 @@  module AWSLambda.Events.SNSEventSpec where +import           AWSLambda.Events.MessageAttribute import           AWSLambda.Events.Records import           AWSLambda.Events.S3Event import           AWSLambda.Events.SNSEvent
+ test/AWSLambda/Events/SQSEventSpec.hs view
@@ -0,0 +1,201 @@+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes       #-}++module AWSLambda.Events.SQSEventSpec where++import           AWSLambda.Events.MessageAttribute+import           AWSLambda.Events.Records+import           AWSLambda.Events.S3Event+import           AWSLambda.Events.SNSEvent+import           AWSLambda.Events.SQSEvent++import           Data.Aeson+import           Data.Aeson.Embedded+import           Data.Aeson.TextValue+import           Data.ByteString.Lazy      (ByteString)+import           Data.Text                 (Text)+import           Data.Time.Calendar+import           Data.Time.Clock+import           Network.AWS.S3            as S3++import           Text.RawString.QQ++import           Test.Hspec++spec :: Spec+spec = describe "Handler" $ do+  it "parses sample text event" $+    decode sampleSQSJSON `shouldBe` Just sampleSQSEvent+  it "parses sample embedded S3 -> SNS -> SQS event" $+    eitherDecode sampleS3SNSSQSJSON `shouldBe` Right sampleS3SNSSQSEvent++sampleSQSJSON :: ByteString+sampleSQSJSON = [r|+{+  "Records": [+    {+      "messageId": "b792b6ba-b444-48c5-9cd8-29b8ad373eae",+      "receiptHandle": "ReceiptHandle",+      "body": "Hello from SQS!",+      "attributes": {+        "ApproximateReceiveCount": "1",+        "SentTimestamp": "1572575717896",+        "SenderId": "AIDAIY4XCTD3OFZN5ED42",+        "ApproximateFirstReceiveTimestamp": "1572575717898"+      },+      "messageAttributes": {+        "Test": {+          "Type": "String",+          "Value": "TestString"+        },+        "TestBinary": {+          "Type": "Binary",+          "Value": "TestBinary"+        }+      },+      "md5OfBody": "aca137746cb7d6a8e3eda3d1ce09b0c5",+      "eventSource": "aws:sqs",+      "eventSourceARN": "arn:aws:sqs:ap-southeast-2:1234567890:my-queue",+      "awsRegion": "ap-southeast-2"+    }+  ]+}+|]++sampleSQSEvent :: SQSEvent Text+sampleSQSEvent =+  RecordsEvent+    [ SQSMessage+        { _sqsmMessageId         = "b792b6ba-b444-48c5-9cd8-29b8ad373eae"+        , _sqsmReceiptHandle     = "ReceiptHandle"+        , _sqsmBody              = "Hello from SQS!"+        , _sqsmAttributes        =+          [ ("ApproximateReceiveCount", "1")+          , ("SentTimestamp", "1572575717896")+          , ("SenderId", "AIDAIY4XCTD3OFZN5ED42")+          , ("ApproximateFirstReceiveTimestamp", "1572575717898")+          ]+        , _sqsmMessageAttributes =+          [ ( "Test"+            , MessageAttribute+              { _maType = "String"+              , _maValue = "TestString"+              })+          , ( "TestBinary"+            , MessageAttribute+              { _maType = "Binary"+              , _maValue = "TestBinary"+              })+          ]+        , _sqsmMd5OfBody         = "aca137746cb7d6a8e3eda3d1ce09b0c5"+        , _sqsmEventSource       = "aws:sqs"+        , _sqsmEventSourceARN    = "arn:aws:sqs:ap-southeast-2:1234567890:my-queue"+        , _sqsmAwsRegion         = Sydney+        }+      ]++sampleS3SNSSQSJSON :: ByteString+sampleS3SNSSQSJSON = [r|+{+  "Records": [+    {+      "messageId": "b792b6ba-b444-48c5-9cd8-29b8ad373eae",+      "receiptHandle": "ReceiptHandle",+      "body": "{\n  \"Type\" : \"Notification\",\n  \"MessageId\" : \"MessageId\",\n  \"TopicArn\" : \"arn:aws:sns:ap-southeast-2:11111111111111:my-topic\",\n  \"Subject\" : \"Amazon S3 Notification\",\n  \"Message\" : \"{\\\"Records\\\":[{\\\"eventVersion\\\":\\\"2.1\\\",\\\"eventSource\\\":\\\"aws:s3\\\",\\\"awsRegion\\\":\\\"ap-southeast-2\\\",\\\"eventTime\\\":\\\"2019-11-01T00:00:00.00Z\\\",\\\"eventName\\\":\\\"ObjectCreated:Put\\\",\\\"userIdentity\\\":{\\\"principalId\\\":\\\"AWS:AHJD568HF4356HJJ:bob\\\"},\\\"requestParameters\\\":{\\\"sourceIPAddress\\\":\\\"787.39.11.220\\\"},\\\"responseElements\\\":{\\\"x-amz-request-id\\\":\\\"GDJS6765sJSHSS\\\",\\\"x-amz-id-2\\\":\\\"ID2\\\"},\\\"s3\\\":{\\\"s3SchemaVersion\\\":\\\"1.0\\\",\\\"configurationId\\\":\\\"ConfigurationId\\\",\\\"bucket\\\":{\\\"name\\\":\\\"my-bucket\\\",\\\"ownerIdentity\\\":{\\\"principalId\\\":\\\"ASKD794UDYDH\\\"},\\\"arn\\\":\\\"arn:aws:s3:::my-bucket\\\"},\\\"object\\\":{\\\"key\\\":\\\"my-key\\\",\\\"size\\\":13315,\\\"eTag\\\":\\\"1231234fabf233124124\\\",\\\"versionId\\\":\\\"735hjf893ufb8fhuf\\\",\\\"sequencer\\\":\\\"HUJKFDHJD8656567HGGSGJKD\\\"}}}]}\",\n  \"Timestamp\" : \"2019-11-01T00:00:00Z\",\n  \"SignatureVersion\" : \"1\",\n  \"Signature\" : \"Signature\",\n  \"SigningCertURL\" : \"https://sns.ap-southeast-2.amazonaws.com/SimpleNotificationService-my-cert.pem\",\n  \"UnsubscribeURL\" : \"https://sns.ap-southeast-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-southeast-2:11111111111111:my-topic:unsub\"\n}",+      "attributes": {+        "ApproximateReceiveCount": "1",+        "SentTimestamp": "1572575717896",+        "SenderId": "AIDAIY4XCTD3OFZN5ED42",+        "ApproximateFirstReceiveTimestamp": "1572575717898"+      },+      "messageAttributes": {+        "Test": {+          "Type": "String",+          "Value": "TestString"+        },+        "TestBinary": {+          "Type": "Binary",+          "Value": "TestBinary"+        }+      },+      "md5OfBody": "aca137746cb7d6a8e3eda3d1ce09b0c5",+      "eventSource": "aws:sqs",+      "eventSourceARN": "arn:aws:sqs:ap-southeast-2:1234567890:my-queue",+      "awsRegion": "ap-southeast-2"+    }+  ]+}+|]++sampleS3SNSSQSEvent :: SQSEvent (Embedded (SNSMessage (Embedded S3Event)))+sampleS3SNSSQSEvent =+  RecordsEvent+    [ SQSMessage+        { _sqsmMessageId         = "b792b6ba-b444-48c5-9cd8-29b8ad373eae"+        , _sqsmReceiptHandle     = "ReceiptHandle"+        , _sqsmBody              =+          TextValue $ Embedded $ SNSMessage+            { _smMessage                = TextValue $ Embedded $ RecordsEvent+              [ S3EventNotification+                { _senAwsRegion         = Sydney+                , _senEventName         = S3ObjectCreatedPut+                , _senEventSource       = "aws:s3"+                , _senEventTime         = UTCTime (fromGregorian 2019 11 1) 0+                , _senEventVersion      = "2.1"+                , _senRequestParameters = RequestParametersEntity "787.39.11.220"+                , _senResponseElements  = ResponseElementsEntity+                  { _reeXAmzId2         = "ID2"+                  , _reeXAmzRequestId   = "GDJS6765sJSHSS" }+                , _senS3                = S3Entity+                  { _seBucket           = S3BucketEntity+                    { _sbeArn           = "arn:aws:s3:::my-bucket"+                    , _sbeName          = BucketName "my-bucket"+                    , _sbeOwnerIdentity = UserIdentityEntity "ASKD794UDYDH" }+                  , _seConfigurationId  = "ConfigurationId"+                  , _seObject           = S3ObjectEntity+                    { _soeETag          = Just (ETag "1231234fabf233124124")+                    , _soeKey           = ObjectKey "my-key"+                    , _soeSize          = Just 13315+                    , _soeSequencer     = "HUJKFDHJD8656567HGGSGJKD"+                    , _soeVersionId     = Just "735hjf893ufb8fhuf" }+                  , _seS3SchemaVersion = "1.0"+                  }+                , _senUserIdentity = UserIdentityEntity "AWS:AHJD568HF4356HJJ:bob"+                }+              ]+            , _smMessageAttributes = mempty+            , _smMessageId         = "MessageId"+            , _smSignature         = "Signature"+            , _smSignatureVersion  = "1"+            , _smSigningCertUrl    = "https://sns.ap-southeast-2.amazonaws.com/SimpleNotificationService-my-cert.pem"+            , _smSubject           = "Amazon S3 Notification"+            , _smTimestamp         = UTCTime (fromGregorian 2019 11 1) 0+            , _smTopicArn          = "arn:aws:sns:ap-southeast-2:11111111111111:my-topic"+            , _smType              = "Notification"+            , _smUnsubscribeUrl    = "https://sns.ap-southeast-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-southeast-2:11111111111111:my-topic:unsub"+          }+        , _sqsmAttributes        =+          [ ("ApproximateReceiveCount", "1")+          , ("SentTimestamp", "1572575717896")+          , ("SenderId", "AIDAIY4XCTD3OFZN5ED42")+          , ("ApproximateFirstReceiveTimestamp", "1572575717898")+          ]+        , _sqsmMessageAttributes =+          [ ( "Test"+            , MessageAttribute+              { _maType = "String"+              , _maValue = "TestString"+              })+          , ( "TestBinary"+            , MessageAttribute+              { _maType = "Binary"+              , _maValue = "TestBinary"+              })+          ]+        , _sqsmMd5OfBody         = "aca137746cb7d6a8e3eda3d1ce09b0c5"+        , _sqsmEventSource       = "aws:sqs"+        , _sqsmEventSourceARN    = "arn:aws:sqs:ap-southeast-2:1234567890:my-queue"+        , _sqsmAwsRegion         = Sydney+        }+      ]