packages feed

stratosphere-0.2.1: examples/apigw-lambda-s3.hs

{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Control.Lens
import Data.Aeson (Value (Array), object)
import qualified Data.ByteString.Lazy.Char8 as B
import Data.Text (Text)
import Stratosphere


-- to curl the endpoint: (substitute your APIGW deployment URL)
-- curl -v -X POST -H "Content-Type: application/json" -d "{\"property\": 3}" "https://t04rmwbtgj.execute-api.us-east-1.amazonaws.com/v1/thing"

main :: IO ()
main = B.putStrLn $ encodeTemplate myTemplate

myTemplate :: Template
myTemplate = template
  [ role
  , incomingS3Bucket
  , lambda
  , permission
  , apiGWRestApi
  , apiGWResource
  , apiGWMethod
  , apiGWDeployment
  ]
  & description ?~ "Simple api gateway attached to a lambda that writes the request body to a S3 object"
  & formatVersion ?~ "2010-09-09"

apiGWRestApi :: Resource
apiGWRestApi = (resource "ApiGWRestApi" $
  ApiGatewayRestApiProperties $
  apiGatewayRestApi
    "myApi"
  )

apiGWResource :: Resource
apiGWResource = (resource "ApiGWResource" $
  ApiGatewayResourceProperties $
  apiGatewayResource
    (GetAtt "ApiGWRestApi" "RootResourceId")
    "thing"
    (toRef apiGWRestApi)
  )

apiGWMethod :: Resource
apiGWMethod = (resource "ApiGWMethod" $
  ApiGatewayMethodProperties $
  apiGatewayMethod
    NONE
    POST
    (toRef apiGWResource)
    (toRef apiGWRestApi)
    & agmeIntegration ?~ integration
    & agmeMethodResponses ?~ [ methodResponse ]
  )
  & dependsOn ?~ deps [
      permission
    ]

  where
    integration = apiGatewayIntegration AWS
      & agiIntegrationHttpMethod ?~ POST
      & agiUri ?~ (Join "" [
          "arn:aws:apigateway:"
        , Ref "AWS::Region"
        , ":lambda:path/2015-03-31/functions/"
        , GetAtt "WriteS3ObjectLambda" "Arn"
        , "/invocations"])
      & agiIntegrationResponses ?~ [ integrationResponse ]
      & agiPassthroughBehavior ?~ WHEN_NO_TEMPLATES
      & agiRequestTemplates ?~ [ ("application/json", "{\"body\": $input.body}") ]

    integrationResponse = apiGatewayIntegrationResponse
      & agirResponseTemplates ?~ [ ("application/json", "$input.json('$.body')") ]
      & agirStatusCode ?~ "200"

    methodResponse = apiGatewayMethodResponse "200"

apiGWDeployment :: Resource
apiGWDeployment = (resource "ApiGWDeployment" $
  ApiGatewayDeploymentProperties $
  apiGatewayDeployment
    (toRef apiGWRestApi)
    & agdStageName ?~ "v1"
  )
  & dependsOn ?~ deps [
      apiGWResource
    , apiGWMethod
    ]

lambda :: Resource
lambda = (resource "WriteS3ObjectLambda" $
  LambdaFunctionProperties $
  lambdaFunction
    lambdaCode
    "index.handler"
    (GetAtt "IAMRole" "Arn")
    NodeJS43
    & lfFunctionName ?~ "writeS3Object"
  )

  where
    lambdaCode :: LambdaFunctionCode
    lambdaCode = lambdaFunctionCode
      & lfcZipFile ?~ code

    code :: Val Text
    code = "\
    \ var AWS = require('aws-sdk');\n\
    \ var s3 = new AWS.S3({apiVersion: '2006-03-01'});\n\
    \ exports.handler = function(event, context, callback) {\n\
    \   console.log(JSON.stringify(event));\n\
    \   s3.putObject({Bucket: \"stratosphere-api-gateway-lambda-incoming\", Key: \"content.json\", ContentType: \"application/json\", Body: JSON.stringify(event.body)}, function(err){\n\
    \     if (err) { \n\
    \       console.log(\"Error:\", err); \n\
    \       callback(err) \n\
    \     } else { \n\
    \       callback(null, {\"body\": event.body});\n\
    \     } \n\
    \   });\n\
    \ }\n\
    \ "

role :: Resource
role = resource "IAMRole" $
  IAMRoleProperties $
  iamRole rolePolicyDocumentObject
  & iamrPolicies ?~ [ executePolicy ]
  & iamrRoleName ?~ "MyLambdaBasicExecutionRole"
  & iamrPath ?~ "/"

  where
    executePolicy =
      iamPolicies
      [ ("Version", "2012-10-17")
      , ("Statement", statement)
      ]
      "MyLambdaExecutionPolicy"

      where
        statement = object
          [ ("Effect", "Allow")
          , ("Action", actions)
          , ("Resource", "*")
          ]

        actions = Array
          [ "logs:CreateLogGroup"
          , "logs:CreateLogStream"
          , "logs:PutLogEvents"
          , "s3:PutObject"
          ]


    rolePolicyDocumentObject =
      [ ("Version", "2012-10-17")
      , ("Statement", statement)
      ]

      where
        statement = object
          [ ("Effect", "Allow")
          , ("Principal", principal)
          , ("Action", "sts:AssumeRole")
          ]

        principal = object
          [ ("Service", "lambda.amazonaws.com") ]

incomingS3Bucket :: Resource
incomingS3Bucket = resource "IncomingBucket" $
  BucketProperties $
  bucket
  & bBucketName ?~ "stratosphere-api-gateway-lambda-incoming"

permission :: Resource
permission = resource "LambdaPermission" $
  LambdaPermissionProperties $
  lambdaPermission
    "lambda:*"
    (GetAtt "WriteS3ObjectLambda" "Arn")
    "apigateway.amazonaws.com"

deps :: [Resource] -> [Text]
deps = map (\d -> d ^. resName)