packages feed

transfer-db-0.3.1.0: src/TransferPlan.hs

{-- |
Module      : TransferPlan
Description : Logic for reading and writing transfer plan configuration options.
Copyright   : (c) Mihai Giurgeanu, 2017
License     : GPL-3
Maintainer  : mihai.giurgeanu@gmail.com
Stability   : experimental
Portability : Portable
--}

-- Allow to use Text literals
{-# LANGUAGE OverloadedStrings #-}

module TransferPlan where

import Data.Yaml.Aeson (ToJSON(toJSON, toEncoding),
                        FromJSON(parseJSON),
                        Value(Object),
                        object,
                        (.=), (.:), (.:?))

import Data.Aeson (pairs)
import Data.Aeson.Types (typeMismatch)

import Data.Semigroup ((<>))

-- | information about the source and destination data sources, about
-- the data that should be transferred and about how to split this data
-- in batches
data TransferPlan = TransferPlan {
  plan_Source                    :: DatabaseScope,      -- ^ defines the source database connection and schema
  plan_Destination               :: DatabaseScope,      -- ^ defines the destination database connection and schema
  plan_Batches                   :: [Batch]             -- ^ defines what data should be transferred between the 2 scopes
  }

instance ToJSON TransferPlan where
  toJSON x     = object ["source" .= plan_Source x,   "destination" .= plan_Destination x,   "batches" .= plan_Batches x]
  toEncoding x = pairs  ("source" .= plan_Source x <> "destination" .= plan_Destination x <> "batches" .= plan_Batches x)

instance FromJSON TransferPlan where
  parseJSON (Object x) = TransferPlan
    <$> x .: "source"
    <*> x .: "destination"
    <*> x .: "batches"
    
  parseJSON invalid = typeMismatch "TransferPlan" invalid
  
-- | information for locating the tables in the source and destination databases
data DatabaseScope = Scope {
  scope_Db                  :: String,  -- ^ data source name
  scope_UserName            :: String,  -- ^ user name to connect to the data source
  scope_Password            :: String,  -- ^ password to connect to the data source
  scope_Schema              :: String   -- ^ the schema for source or destination tables
  }

instance ToJSON DatabaseScope where
  toJSON x     = object ["db" .= scope_Db x,   "user" .= scope_UserName x,   "password" .= scope_Password x,   "schema" .= scope_Schema x]
  toEncoding x = pairs  ("db" .= scope_Db x <> "user" .= scope_UserName x <> "password" .= scope_Password x <> "schema" .= scope_Schema x)
  
instance FromJSON DatabaseScope where
  parseJSON (Object x) = do
    db          <- x .: "db"
    user'       <- x .:? "user"
    password'   <- x .:? "password"
    schema'     <- x .:? "schema"
    let user     = maybe ""   id user'
        password = maybe ""   id password'
        schema   = maybe user id schema'
    return $ Scope db user password schema

  parseJSON invalid = typeMismatch "DatabaseScope" invalid

data Batch = Batch {
  batch_Name   :: String,       -- ^ a name identifying the batch to the user; it is used in log messages
  batch_Items  :: [BatchItem]   -- ^ each batch item defines what data in a table will be transferred in that batch
  }

instance ToJSON Batch where
  toJSON     x = object ["name" .= batch_Name x,   "items" .= batch_Items x]
  toEncoding x = pairs  ("name" .= batch_Name x <> "items" .= batch_Items x)

instance FromJSON Batch where
  parseJSON (Object x) = Batch
    <$> x .: "name"
    <*> x .: "items"

  parseJSON invalid = typeMismatch "Batch" invalid

data BatchItem = BatchItem {
  batch_Table           :: String,              -- ^ the table name in the source database
  batch_OrderBy         :: Maybe [ColumnName],  -- ^ the columns the query should be ordered by; usually the keys by witch we split the batch
  batch_Where           :: Maybe WhereCondition -- ^ the where clause to select the records in this batch
  }

instance ToJSON BatchItem where
  toJSON     x = object ["table" .= batch_Table x,   "orderBy" .= batch_OrderBy x,   "where" .= batch_Where x]
  toEncoding x = pairs  ps
    where ps = case batch_Where x of
            Nothing -> psOrderAndTable
            y -> psOrderAndTable <> "where" .= y
          psOrderAndTable = case batch_OrderBy x of
            Nothing -> psTable
            y -> psTable <> "orderBy" .= y
          psTable = "table" .= batch_Table x 

instance FromJSON BatchItem where
  parseJSON (Object x) = do
    table       <- x .:  "table"
    orderBy     <- x .:? "orderBy"
    whereClause <- x .:? "where"
    
    let orderBy'     = maybe Nothing listOrNothing orderBy
        whereClause' = maybe Nothing listOrNothing whereClause
    return $ BatchItem table orderBy' whereClause'
    
  parseJSON invalid = typeMismatch "BatchOption" invalid

-- | returns Nothing for an empty parameter list
listOrNothing :: [a] -> Maybe [a]
listOrNothing [] = Nothing
listOrNothing x  = Just x

type ColumnName = String
type WhereCondition = String