content: {-# LANGUAGE DataKinds        #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE TypeOperators    #-}
  
  module Qi.Test.Integration.SimpleS3Copy where
  
  import           Control.Lens
  import           Control.Monad.Freer
  import           Control.Monad.Freer.State
  import qualified Data.ByteString.Lazy          as LBS
  import           Data.Default                  (def)
  import           Protolude                     hiding (State, getAll, runState)
  import           Qi.AWS.Types                  (AwsMode (LocalStack))
  import           Qi.CLI.Dispatcher
  import           Qi.Config.AWS
  import           Qi.Config.AWS.Lambda          (LambdaMemorySize (..),
                                                  lpMemorySize)
  import           Qi.Config.AWS.S3
  import           Qi.Config.AWS.S3.Accessors    (getIdByName)
  import qualified Qi.Config.CfTemplate          as CF
  import           Qi.Config.Identifier          (S3BucketId)
  import qualified Qi.Program.Config.Ipret.State as Config
  import           Qi.Program.Config.Lang        (ConfigEff, s3Bucket,
                                                  s3BucketLambda)
  import           Qi.Program.Gen.Lang           (GenEff, say, sleep)
  import qualified Qi.Program.Gen.Lang           as Gen
  import           Qi.Program.S3.Lang            (S3Eff, S3LambdaProgram,
                                                  getContent, putContent)
  import qualified Qi.Program.Wiring.IO          as IO
  import           Qi.Test.Logger
  import           System.RandomString
  import           Test.Hspec
  
  
  configProgram
    :: Member ConfigEff effs
    => Eff effs ()
  configProgram = do
  
    -- create an "input" s3 bucket
    incoming <- s3Bucket "incoming" def
  
    -- create an "output" s3 bucket
    outgoing <- s3Bucket "outgoing" def
  
    -- create a lambda, which will copy an s3 object from "incoming" to "outgoing" buckets
    -- upon an S3 "Put" event.
    -- Attach the lambda to the "incoming" bucket such way so each time a file is uploaded to
    -- the bucket, the lambda is called with the information about the newly uploaded file.
    -- The lambda creation function takes the Lambda name, s3BucketId to attach to, lambda
    -- function itself and a lambda profile, that specifies attributes like memory size and
    -- timeout, and has meaningful defaults for those.
    void $ s3BucketLambda "copyS3Object" incoming (copyContentsLambda outgoing) $
      def & lpMemorySize .~ M1536
  
    where
      copyContentsLambda
        :: (Member S3Eff effs, Member GenEff effs)
        => S3BucketId
        -> S3LambdaProgram effs
      copyContentsLambda sinkBucketId = lbd
        where
          lbd event = do
            let incomingS3Obj = event ^. s3eObject
                outgoingS3Obj = s3oBucketId .~ sinkBucketId $ incomingS3Obj
  
            say "getting content"
            -- get the content of the newly uploaded file
            eitherContent <- getContent incomingS3Obj
  
            case eitherContent of
              Right content -> do
                say "putting content"
                -- write the content into a new file in the "output" bucket
                putContent outgoingS3Obj content
  
                pure "lambda had executed successfully"
  
              Left err ->
                pure . toS $ "error: '" <> err <> "'"
  
  
  spec :: Spec
  spec = parallel $ do
    describe "Integration" $ do
  
      let config =
              snd
            . run
            . runState def{_namePrefix = "qmuli-test"}
            . Config.run
            $ do
              {- s3Bucket "app" -- always assume existence of the app bucket -}
              configProgram
  
          template = CF.render config
  
      it "copies the file correctly" $ do
        filename <- randomString (StringOpts Base64 8)
        content <- randomString (StringOpts Base64 1024)
  
        result <- do
          -- deploy the stack
              IO.run config LocalStack mkTestLogger $ do
                let incoming = s3Object (getIdByName config "incoming") $ S3Key filename
                    outgoing = s3Object (getIdByName config "outgoing") $ S3Key filename
  
                {- createCfStack template -}
  
                -- upload a file into the incoming bucket
                putContent incoming content
  
                sleep 1000000
  
                -- download the copied file from the outgoing bucket
                getContent outgoing
  
  
        -- verify that the file has been copied correctly
        result `shouldBe` Right content
  
  {-
      it "works" $ do
        let content = "my content" :: Text
  
        result <- do
          -- deploy the stack
              IO.run config LocalStack $ do
                builtLambda <- Gen.readFileLazy =<< Gen.build
                deployApp template builtLambda
                createCfStack template
                pure content
  
          -- upload a file into the incoming bucket
          -- download the copied file from the outgoing bucket
          -- verify that the file has been copied correctly
  
        result `shouldBe` content
  -}
  