packages feed

serverless-haskell 0.10.5 → 0.11.2

raw patch · 5 files changed

+82/−51 lines, 5 filesdep +http-clientdep +safe-exceptionsdep −networkdep −network-simple

Dependencies added: http-client, safe-exceptions

Dependencies removed: network, network-simple

Files

README.md view
@@ -121,8 +121,13 @@ [AWSLambda.Events.APIGateway](https://hackage.haskell.org/package/serverless-haskell/docs/AWSLambda-Events-APIGateway.html) in the handler to process them. -[Serverless Offline] can be used for local testing of API Gateway requests.+[Serverless Offline] can be used for local testing of API Gateway requests. You+must use `--useDocker` flag so that the native Haskell runtime works correctly. +When using [Serverless Offline], make sure that the project directory is+world-readable, otherwise the started Docker container will be unable to access+the handlers and all invocations will return HTTP status 502.+ ### Notes  * Only AWS Lambda is supported at the moment. Other cloud providers would@@ -153,8 +158,14 @@ Integration test is only automatically run up to deployment due to the need for an AWS account. To run manually: -* Ensure you have the required dependencies: `curl`, [jq], [NPM], `pwgen` and-  [Stack].+* Ensure you have the required dependencies:+  - `curl`+  - [jq]+  - `libpcre` headers (`-devel` package or similar)+  - [NPM]+  - [`pkg-config`](pkg-config)+  - `pwgen`+  - [Stack] * Get an AWS account and add the access credentials into your shell environment. * Run `./integration-test/run.sh`. The exit code indicates success. * To verify just the packaging, without deployment, run@@ -180,6 +191,7 @@ [Docker]: https://www.docker.com/ [jq]: https://stedolan.github.io/jq/ [NPM]: https://www.npmjs.com/+[pkg-config]: https://www.freedesktop.org/wiki/Software/pkg-config/ [Serverless]: https://serverless.com/framework/ [Serverless Offline]: https://github.com/dherault/serverless-offline [Stack]: https://haskellstack.org
serverless-haskell.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.31.2.+-- This file has been generated from package.yaml by hpack version 0.33.0. -- -- see: https://github.com/sol/hpack ----- hash: a0188a30ab4b0a3456e1359270cfe59d4445645d2de25f44bc3c3a1ccdf9a551+-- hash: 81b4069c39b4cebbf975a9e2d1fc55161e337ed88e6a6f0cf45373663399f0b5  name:           serverless-haskell-version:        0.10.5+version:        0.11.2 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@@ -54,11 +54,11 @@     , base >=4.7 && <5     , bytestring     , case-insensitive+    , http-client     , http-types     , iproute     , lens-    , network-    , network-simple+    , safe-exceptions     , text     , time     , unix@@ -92,12 +92,12 @@     , case-insensitive     , hspec     , hspec-discover+    , http-client     , http-types     , iproute     , lens-    , network-    , network-simple     , raw-strings-qq+    , safe-exceptions     , serverless-haskell     , text     , time
src/AWSLambda.hs view
@@ -55,7 +55,8 @@ handler to process them.  <https://github.com/dherault/serverless-offline Serverless Offline> can be used-for local testing of API Gateway requests.+for local testing of API Gateway requests. You must use @--useDocker@ flag so+that the native Haskell runtime works correctly.  = Additional features @@ -70,17 +71,6 @@     >     stackBuildArgs:     >       - --pedantic     >       - --allow-different-user--* To start the executable with extra arguments, add them to @arguments@ under-the function name:--    > custom:-    >   haskell:-    >     arguments:-    >       myfunc:-    >         - --arg1-    >         - --arg2-    >         - arg3  * Dependent system libraries not present in the AWS Lambda environment will be automatically uploaded along with the executable. Note that while statically
src/AWSLambda/Events/APIGateway.hs view
@@ -37,7 +37,6 @@ import           Data.HashMap.Strict     (HashMap) import qualified Data.HashMap.Strict     as HashMap import           Data.IP-import           Data.Text               (Text) import qualified Data.Text               as Text import           Data.Text.Encoding      (decodeUtf8, encodeUtf8) import           GHC.Generics            (Generic)
src/AWSLambda/Handler.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ {-| Module      : AWSLambda.Handler Stability   : experimental@@ -9,13 +11,16 @@   ( lambdaMain   ) where -import Control.Exception (finally)-import Control.Monad (void)+import Control.Exception.Safe (SomeException(..), displayException, tryAny)+import Control.Monad (forever, void) +import Data.Aeson ((.=)) import qualified Data.Aeson as Aeson -import Data.ByteString (ByteString)+import Data.Typeable (typeOf)+ import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Char8 as Char8 import qualified Data.ByteString.Lazy as LBS  import qualified Data.Text.Encoding as Text@@ -23,9 +28,8 @@  import GHC.IO.Handle (BufferMode(..), hSetBuffering) -import Network.Simple.TCP (connect)-import Network.Socket (close, withSocketsDo)-import Network.Socket.ByteString (send)+import Network.HTTP.Client+import Network.HTTP.Types (HeaderName)  import System.Environment (lookupEnv) import System.IO (stdout)@@ -84,31 +88,57 @@   => (event -> IO res) -- ^ Function to process the event   -> IO () lambdaMain act =-  withSendResult $ \sendResult -> do-    input <- ByteString.getLine-    case Aeson.eitherDecodeStrict input of+  runMain $ \input -> do+    case Aeson.eitherDecode input of       Left err -> error err       Right event -> do         result <- act event-        sendResult $ LBS.toStrict $ Aeson.encode result-        pure ()+        pure $ Aeson.encode result --- | Invoke an action with the method to output the result. If called by the--- JavaScript wrapper, use the server started by it, otherwise use standard--- output. Also set line buffering on standard output for AWS Lambda so the logs--- are output in a timely manner.-withSendResult :: ((ByteString -> IO ()) -> IO r) -> IO r-withSendResult act = do-  communicationPortString <- lookupEnv communicationPortEnv-  case communicationPortString of-    Just communicationPort -> do+-- Process the incoming requests (using the AWS Lambda runtime interface or from the standard input).+-- Also set line buffering on standard output for AWS Lambda so the logs are output in a timely manner.+runMain :: (LBS.ByteString -> IO LBS.ByteString) -> IO ()+runMain act = do+  lambdaApiAddress <- lookupEnv lambdaApiAddressEnv+  case lambdaApiAddress of+    Just address -> do       hSetBuffering stdout LineBuffering-      withSocketsDo $-        connect "127.0.0.1" communicationPort $ \(socket, _) ->-          act (void . send socket) `finally` close socket-    Nothing -> act $ Text.putStrLn . Text.decodeUtf8+      manager <- newManager defaultManagerSettings+      forever $ do+        invocation <- httpLbs (invocationRequest address) manager+        let input = responseBody invocation+        let requestId = responseRequestId invocation+        resultOrError <- tryAny $ act input+        case resultOrError of+          Right result -> void $ httpNoBody (resultRequest address requestId result) manager+          Left exception -> void $ httpNoBody (errorRequest address requestId exception) manager+    Nothing -> do+      input <- LBS.fromStrict <$> ByteString.getLine+      result <- act input+      Text.putStrLn $ Text.decodeUtf8 $ LBS.toStrict result --- | Environment variable signalling the port the JavaScript wrapper is--- listening on-communicationPortEnv :: String-communicationPortEnv = "SERVERLESS_HASKELL_COMMUNICATION_PORT"+lambdaApiAddressEnv :: String+lambdaApiAddressEnv = "AWS_LAMBDA_RUNTIME_API"++lambdaRequest :: String -> String -> Request+lambdaRequest apiAddress rqPath = parseRequest_ $ "http://" ++ apiAddress ++ "/2018-06-01" ++ rqPath++invocationRequest :: String -> Request+invocationRequest apiAddress = (lambdaRequest apiAddress "/runtime/invocation/next") { responseTimeout = responseTimeoutNone }++resultRequest :: String -> String -> LBS.ByteString -> Request+resultRequest apiAddress requestId result = (lambdaRequest apiAddress $ "/runtime/invocation/" ++ requestId ++ "/response") { method = "POST", requestBody = RequestBodyLBS result }++errorRequest :: String -> String -> SomeException -> Request+errorRequest apiAddress requestId exception = (lambdaRequest apiAddress $ "/runtime/invocation/" ++ requestId ++ "/error") { method = "POST", requestBody = RequestBodyLBS body }+  where+    body = Aeson.encode $ Aeson.object [ "errorMessage" .= displayException exception, "errorType" .= exceptionType exception]++exceptionType :: SomeException -> String+exceptionType (SomeException e) = show (typeOf e)++requestIdHeader :: HeaderName+requestIdHeader = "Lambda-Runtime-Aws-Request-Id"++responseRequestId :: Response a -> String+responseRequestId = Char8.unpack . snd . head . filter (uncurry $ \h _ -> h == requestIdHeader) . responseHeaders