eventstore-1.4.0: Database/EventStore/Internal/Operation.hs
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE StandaloneDeriving #-}
--------------------------------------------------------------------------------
-- |
-- Module : Database.EventStore.Internal.Operation
-- Copyright : (C) 2020 Yorick Laupa
-- License : (see the file LICENSE)
-- Maintainer: Yorick Laupa <yo.eight@gmail.com>
-- Stability : experimental
-- Portability: non-portable
--
--------------------------------------------------------------------------------
module Database.EventStore.Internal.Operation where
--------------------------------------------------------------------------------
import Prelude (String)
import Data.ProtocolBuffers
import Data.Serialize (runPut, runGet)
--------------------------------------------------------------------------------
import Database.EventStore.Internal.Command
import Database.EventStore.Internal.Control
import Database.EventStore.Internal.Prelude hiding ((.), id)
import Database.EventStore.Internal.Stream
import Database.EventStore.Internal.Types
--------------------------------------------------------------------------------
newtype Mailbox = Mailbox (Chan (Either OperationError Package))
--------------------------------------------------------------------------------
mailboxSendPkg :: MonadBase IO m => Mailbox -> Package -> m ()
mailboxSendPkg (Mailbox chan) pkg = writeChan chan (Right pkg)
--------------------------------------------------------------------------------
mailboxFail :: MonadBase IO m => Mailbox -> OperationError -> m ()
mailboxFail (Mailbox chan) e = writeChan chan (Left e)
--------------------------------------------------------------------------------
mailboxRead :: MonadBase IO m => Mailbox -> m (Either OperationError Package)
mailboxRead (Mailbox chan) = readChan chan
--------------------------------------------------------------------------------
mailboxReadDecoded
:: (MonadBase IO m, Decode resp)
=> Mailbox
-> m (Either OperationError resp)
mailboxReadDecoded (Mailbox chan)
= fmap (decodePkg =<<) $ readChan chan
--------------------------------------------------------------------------------
mailboxNew :: MonadBase IO m => m Mailbox
mailboxNew = Mailbox <$> newChan
--------------------------------------------------------------------------------
createPkg
:: (Encode msg, MonadIO m)
=> Command
-> Maybe Credentials
-> msg
-> m Package
createPkg cmd creds msg
= do pkgId <- freshUUID
let dat = runPut $ encodeMessage msg
pkg
= Package
{ packageCmd = cmd
, packageCorrelation = pkgId
, packageData = dat
, packageCred = creds
}
pure pkg
--------------------------------------------------------------------------------
-- FIXME We could use Bifunctor but can't I am not sure it covers all the GHC
-- we support at that time.
decodePkg :: Decode msg => Package -> Either OperationError msg
decodePkg pkg
= case runGet decodeMessage (packageData pkg) of
Left e -> Left $ ProtobufDecodingError e
Right resp -> Right resp
--------------------------------------------------------------------------------
-- | Operation exception that can occurs on an operation response.
data OperationError
= WrongExpectedVersion !Text !ExpectedVersion -- ^ Stream and Expected Version
| StreamDeleted !StreamName -- ^ Stream
| InvalidTransaction
| forall t. AccessDenied !(StreamId t) -- ^ Stream
| InvalidServerResponse !Command !Command -- ^ Expected, Found
| ProtobufDecodingError !String
| ServerError !(Maybe Text) -- ^ Reason
| InvalidOperation !Text
| StreamNotFound !StreamName
| NotAuthenticatedOp
-- ^ Invalid operation state. If happens, it's a driver bug.
| Aborted
-- ^ Occurs when the user asked to close the connection or if the
-- connection can't reconnect anymore.
| ConnectionHasDropped
deriving Typeable
--------------------------------------------------------------------------------
deriving instance Show OperationError
--------------------------------------------------------------------------------
instance Exception OperationError
--------------------------------------------------------------------------------
-- | Operation result sent by the server.
data OpResult
= OP_SUCCESS
| OP_PREPARE_TIMEOUT
| OP_COMMIT_TIMEOUT
| OP_FORWARD_TIMEOUT
| OP_WRONG_EXPECTED_VERSION
| OP_STREAM_DELETED
| OP_INVALID_TRANSACTION
| OP_ACCESS_DENIED
deriving (Eq, Enum, Show)
--------------------------------------------------------------------------------
data Lifetime
= OneTime
| KeepAlive !Command
--------------------------------------------------------------------------------
data Loop a
= Loop
| Break !a
--------------------------------------------------------------------------------
data LoopS s a
= LoopS !s
| BreakS !a
--------------------------------------------------------------------------------
keepLooping :: Monad m => m (Loop a) -> m a
keepLooping action
= go
where
go = do result <- action
case result of
Loop -> go
Break a -> pure a
--------------------------------------------------------------------------------
keepLoopingS :: Monad m => s -> (s -> m (LoopS s a)) -> m a
keepLoopingS seed action
= go seed
where
go cur
= do result <- action cur
case result of
LoopS next
-> go next
BreakS a
-> pure a