{-# LANGUAGE DataKinds #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_HADDOCK prune not-home #-} {-| Copyright : (c) 2020-2021 Tim Emiola SPDX-License-Identifier: BSD3 Maintainer : Tim Emiola Provides an instance of 'Proc' that launches @postgres@ as a @tmp proc@. The instance this module provides can be used in integration tests as is. It's also possible to write other instances that launch @postgres@ in different ways; for those, this instance can be used as a reference example. -} module System.TmpProc.Docker.Postgres ( -- * 'Proc' instance TmpPostgres(..) -- * Useful definitions , aProc , aHandle -- * Re-exports , module System.TmpProc ) where import Control.Exception (catch) import qualified Data.ByteString.Char8 as C8 import Data.String (fromString) import Data.Text (Text) import qualified Data.Text as Text import Database.PostgreSQL.Simple (Connection, SqlError, close, connectPostgreSQL, execute_) import System.TmpProc (Connectable (..), HList (..), HandlesOf, HostIpAddress, Pinged (..), Proc (..), ProcHandle (..), SvcURI, startupAll, withTmpConn) {-| A singleton 'HList' containing a 'TmpPostgres'. -} aProc :: HList '[TmpPostgres] aProc = TmpPostgres [] `HCons` HNil {-| An 'HList' that contains the handle created from 'aProc'. -} aHandle :: IO (HandlesOf '[TmpPostgres]) aHandle = startupAll aProc {-| Provides the capability to launch a Postgres database as a @tmp proc@. The constructor receives the names of the tables to be dropped on 'reset'. -} data TmpPostgres = TmpPostgres [Text] {-| Specifies how to run @postgres@ as a @tmp proc@. -} instance Proc TmpPostgres where type Image TmpPostgres = "postgres:10.6" type Name TmpPostgres = "a-postgres-db" uriOf = mkUri' runArgs = runArgs' ping = toPinged . connectPostgreSQL . hUri reset = reset' {-| Specifies how to connect to a tmp @postgres@ db. -} instance Connectable TmpPostgres where type Conn TmpPostgres = Connection openConn = connectPostgreSQL . hUri closeConn = close {-| Makes a uri whose password matches the one specified in 'runArgs''. -} mkUri' :: HostIpAddress -> SvcURI mkUri' ip = "postgres://postgres:" <> dbPassword <> "@" <> (C8.pack (Text.unpack ip)) <> "/postgres" dbPassword :: C8.ByteString dbPassword = "mysecretpassword" {-| Match the password used in 'mkUri''. -} runArgs' :: [Text] runArgs' = [ "-e" , "POSTGRES_PASSWORD=" <> Text.pack (C8.unpack dbPassword) ] toPinged :: IO a -> IO Pinged toPinged action = ((action >> pure OK) `catch` (\(_ :: SqlError) -> pure NotOK)) `catch` (\(_ :: IOError) -> pure NotOK) {-| Empty all rows in the tables, if any are specified. -} reset' :: ProcHandle TmpPostgres -> IO () reset' handle@(ProcHandle {hProc}) = let go (TmpPostgres []) = pure () go (TmpPostgres tables) = withTmpConn handle $ \c -> mapM_ (execute_ c . (fromString . (++) "DELETE FROM ") . Text.unpack) tables in go hProc