{-# LANGUAGE DataKinds #-} {-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE TypeFamilies #-} module NestedSpec where import Control.Exception.Safe (catchAny, finally) import Control.Monad.IO.Class (liftIO) import Control.Lens (makeLenses, (^.), (^?), ix, (^..)) import Data.Function ((&)) import Data.Hashable import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HMap import Data.Proxy import qualified Data.Set as Set import Data.Tagged import qualified Data.Text as T import Network.AWS import Network.AWS.DynamoDB (dynamoDB) import System.Environment (setEnv) import System.IO (stdout) import Test.Hspec import Database.DynamoDB import Database.DynamoDB.Filter import Database.DynamoDB.TH import Database.DynamoDB.Update data TName type Name = Tagged TName T.Text instance Hashable Name data Inner = Inner { _nFirst :: T.Text , _nSecond :: Maybe Int , _nThird :: T.Text } deriving (Show, Eq) deriveCollection ''Inner defaultTranslate makeLenses ''Inner data Test = Test { _iHashKey :: T.Text , _iRangeKey :: Int , _iInner :: Inner , _iMInner :: Maybe Inner , _iSet :: Set.Set Name , _iList :: [Inner] , _iMap :: HashMap Name Inner } deriving (Show, Eq) mkTableDefs "migrateTest" (tableConfig "" (''Test, WithRange) [] []) withDb :: Example (IO b) => String -> AWS b -> SpecWith (Arg (IO b)) withDb msg code = it msg runcode where runcode = do setEnv "AWS_ACCESS_KEY_ID" "XXXXXXXXXXXXXX" setEnv "AWS_SECRET_ACCESS_KEY" "XXXXXXXXXXXXXXfdjdsfjdsfjdskldfs+kl" lgr <- newLogger Debug stdout env <- newEnv Discover let dynamo = setEndpoint False "localhost" 8000 dynamoDB let newenv = env & configure dynamo -- & set envLogger lgr runResourceT $ runAWS newenv $ do deleteTable (Proxy :: Proxy Test) `catchAny` (\_ -> return ()) migrateTest mempty Nothing code `finally` deleteTable (Proxy :: Proxy Test) spec :: Spec spec = do describe "Nested structures" $ do withDb "Save and retrieve" $ do let inner1 = Inner "test" (Just 3) "test" inner2 = Inner "" Nothing "" testitem1 = Test "hash" 1 inner1 (Just inner2) (Set.singleton (Tagged "test")) [inner1] (HMap.singleton "test" inner2) testitem2 = Test "hash" 2 inner1 Nothing Set.empty [] HMap.empty putItem testitem1 putItem testitem2 Just ritem1 <- getItem Strongly tTest ("hash", 1) Just ritem2 <- getItem Strongly tTest ("hash", 2) liftIO $ testitem1 `shouldBe` ritem1 liftIO $ testitem2 `shouldBe` ritem2 withDb "Scan conditions" $ do let inner1 = Inner "test" (Just 3) "" inner2 = Inner "" Nothing "test" testitem1 = Test "hash" 1 inner1 (Just inner2) (Set.singleton (Tagged "test")) [inner1] (HMap.singleton "test" inner2) testitem2 = Test "hash" 2 inner2 Nothing Set.empty [] HMap.empty putItem testitem1 putItem testitem2 -- items1 <- scanCond tTest (iInner' <.> nFirst' ==. "test") 10 liftIO $ items1 `shouldBe` [testitem1] -- items2 <- scanCond tTest (iInner' <.> nFirst' ==. "") 10 liftIO $ items2 `shouldBe` [testitem2] -- items3 <- scanCond tTest (iMInner' <.> nThird' ==. "test") 10 liftIO $ items3 `shouldBe` [testitem1] -- items4 <- scanCond tTest (iMInner' ==. Nothing) 10 liftIO $ items4 `shouldBe` [testitem2] -- items5 <- scanCond tTest (iSet' `setContains` Tagged "test") 10 liftIO $ items5 `shouldBe` [testitem1] -- items6 <- scanCond tTest (iList' 0 <.> nFirst' ==. "test") 10 liftIO $ items6 `shouldBe` [testitem1] -- items7 <- scanCond tTest (iMap' Tagged "test" <.> nThird' ==. "test") 10 liftIO $ items7 `shouldBe` [testitem1] withDb "Nested updates" $ do let inner1 = Inner "test" (Just 3) "" inner2 = Inner "" Nothing "test" testitem1 = Test "hash" 1 inner1 (Just inner2) (Set.singleton (Tagged "test")) [inner1] (HMap.singleton "test" inner2) testitem2 = Test "hash" 2 inner2 Nothing Set.empty [] HMap.empty putItem testitem1 putItem testitem2 -- newitem1 <- updateItemByKey tTest (tableKey testitem1) (iInner' <.> nFirst' =. "updated") liftIO $ newitem1 ^. iInner . nFirst `shouldBe` "updated" -- newitem2 <- updateItemByKey tTest (tableKey testitem1) (add iSet' (Set.singleton (Tagged "test2"))) liftIO $ newitem2 ^. iSet `shouldBe` Set.fromList [Tagged "test", Tagged "test2"] -- newitem3 <- updateItemByKey tTest (tableKey testitem1) (prepend iList' [inner2, inner1]) liftIO $ newitem3 ^. iList `shouldBe` [inner2, inner1, inner1] -- newitem4 <- updateItemByKey tTest (tableKey testitem1) (delListItem iList' 1) liftIO $ newitem4 ^. iList `shouldBe` [inner2, inner1] -- newitem5 <- updateItemByKey tTest (tableKey testitem2) (iMap' Tagged "test" =. inner1) liftIO $ newitem5 ^.. iMap . traverse `shouldBe` [inner1] liftIO $ newitem5 ^? iMap . ix (Tagged "test") `shouldBe` Just inner1 -- newitem6 <- updateItemByKey tTest (tableKey testitem2) (delHashKey iMap' (Tagged "test")) liftIO $ newitem6 ^. iMap `shouldBe` HMap.empty main :: IO () main = hspec spec