packages feed

htsn-import-0.2.2: src/TSN/DbImport.hs

-- | Definition of the DbImport typeclass.
--
--   When we parse an XML tree, there are two functions that we would
--   like to call on the result independent of its type. First, we
--   would like to be able to run the database migrations for that
--   type. The migrations are kept separate from insertion because, at
--   some later point, it make make sense to disable automatic
--   migrations.
--
--   Next we want to import the thing.
--
--   Neither of these should depend on the type -- we should just be
--   able to call 'dbmigrate' followed by 'dbimport' on the
--   datastructure and have the right thing happen. That is the
--   purpose of the 'DbImport' typeclass. It allows the XML types to
--   define their own \"migrate me\" and \"insert me\" functions that
--   the rest of the application doesn't have to care about.
--
module TSN.DbImport (
  DbImport(..),
  ImportResult(..),
  run_dbmigrate )
where

-- System imports
import Control.Monad ( forM_ )
import Control.Monad.IO.Class ( MonadIO( liftIO ) )
import qualified Data.Map as Map ( elems )
import Database.Groundhog ( executeRaw )
import Database.Groundhog.Generic (
  createMigration,
  getQueries,
  mergeMigrations )
import Database.Groundhog.Core ( Migration, NamedMigrations, PersistBackend )
import Network.Services.TSN.Report ( report_info )


-- | The type that will be returned from every file import attempt.
--
data ImportResult =
  ImportFailed String -- ^ Failure with an error message.

  | ImportSkipped String -- ^ We processed the file, but didn't import it.
                         --   The reason is contained in the second field.

  | ImportSucceeded      -- ^ We did import records.

  | ImportUnsupported String -- ^ We didn't know how to process this file.
                             --   The second field should contain info.


-- | Instances of this type know how to run their own database
--   migrations and insert themselves into a database.
--
class DbImport a where
  -- | Import an instance of type @a@.
  dbimport :: (PersistBackend m) => a -> m ImportResult

  -- | This must migrate *all* stuffs that can potentially be
  --   created/used by the type @a@.
  dbmigrate :: (MonadIO m, PersistBackend m) => a -> m ()


-- | A migration runner that will use our normal info reporting
--   mechanism. The top-level code was stolen from 'runMigration' in
--   "Data.Groundhog.Generic" and the 'execute_pretty' code was stolen
--   from 'executeMigration'' in the same module.
--
run_dbmigrate :: (MonadIO m, PersistBackend m) => Migration m -> m ()
run_dbmigrate migration = createMigration migration >>= execute_pretty
  where
    execute_pretty :: (PersistBackend m, MonadIO m) => NamedMigrations -> m ()
    execute_pretty m = do
      let migs = getQueries False $ mergeMigrations $ Map.elems m
      case migs of
        Left errs -> fail $ unlines errs
        Right qs -> forM_ qs $ \q -> do
                         liftIO $ report_info ("Migration: " ++ q ++ ";")
                         executeRaw False q []