packages feed

poppy-codegen-1.0.0: test/Support/TestMigrations.hs

module Support.TestMigrations
  ( runTestMigrations,
  )
where

import Control.Exception (Handler (..), catches, throwIO)
import Control.Monad (forM_, void)
import qualified Data.ByteString.Char8 as B8
import Data.Char (isSpace)
import Data.List (sort)
import Database.PostgreSQL.Simple (Connection, SqlError (..), connectPostgreSQL, execute_)
import Database.PostgreSQL.Simple.Types (Query (..))
import System.Directory (listDirectory)
import System.FilePath ((</>))

testMigrationsDir :: FilePath
testMigrationsDir = "test/migrations"

runTestMigrations :: String -> IO ()
runTestMigrations databaseUrl = do
  conn <- connectPostgreSQL $ B8.pack databaseUrl
  names <- sort <$> listDirectory testMigrationsDir
  forM_ names $ \name -> do
    sql <- B8.readFile (testMigrationsDir </> name)
    forM_ (splitStatements sql) $ \stmt ->
      executeIgnoringDuplicate conn stmt

splitStatements :: B8.ByteString -> [B8.ByteString]
splitStatements =
  filter (not . B8.null) . map stripBytes . B8.split ';'

executeIgnoringDuplicate :: Connection -> B8.ByteString -> IO ()
executeIgnoringDuplicate conn stmt =
  void (execute_ conn (Query stmt))
    `catches` [Handler ignoreDuplicate]
  where
    ignoreDuplicate err
      | sqlState err == "42710" = pure ()
      | sqlState err == "42P07" = pure () -- relation already exists (replayed UNIQUE)
      | otherwise = throwIO err

stripBytes :: B8.ByteString -> B8.ByteString
stripBytes =
  B8.reverse . B8.dropWhile isSpace . B8.reverse . B8.dropWhile isSpace