packages feed

supernova-0.0.3: src/Pulsar/Core.hs

{-# LANGUAGE OverloadedStrings #-}

{- Defines a set of transactional commands, communicating via internal channels -}
module Pulsar.Core where

import           Control.Concurrent.Chan
import           Control.Concurrent.MVar
import           Control.Exception              ( throwIO )
import           Control.Monad.Catch            ( MonadThrow )
import           Control.Monad.IO.Class
import           Data.Text                      ( Text )
import           Lens.Family
import           Proto.PulsarApi         hiding ( Subscription )
import qualified Proto.PulsarApi_Fields        as F
import           Pulsar.AppState
import           Pulsar.Connection
import           Pulsar.Internal.Logger
import qualified Pulsar.Protocol.Commands      as P
import           Pulsar.Protocol.Frame          ( Payload(..)
                                                , Response(..)
                                                , getCommand
                                                )
import           Pulsar.Types

------ Simple commands ------

lookup :: Connection -> MVar Response -> ReqId -> Topic -> IO ()
lookup (Conn s) var (ReqId req) topic = do
  logRequest $ P.lookup req topic
  sendSimpleCmd s $ P.lookup req topic
  -- TODO: we need to analyze it and might need to re-issue another lookup
  readMVar var >>= logResponse

newProducer
  :: Connection -> MVar Response -> ReqId -> ProducerId -> Topic -> IO Text
newProducer (Conn s) var (ReqId req) (PId pid) topic = do
  logRequest $ P.producer req pid topic
  sendSimpleCmd s $ P.producer req pid topic
  resp <- readMVar var
  logResponse resp
  case getCommand resp ^. F.maybe'producerSuccess of
    Just ps -> return $ ps ^. F.producerName
    Nothing -> return ""

closeProducer :: Connection -> MVar Response -> ProducerId -> ReqId -> IO ()
closeProducer (Conn s) var (PId pid) (ReqId req) = do
  logRequest $ P.closeProducer req pid
  sendSimpleCmd s $ P.closeProducer req pid
  readMVar var >>= logResponse

newSubscriber
  :: Connection
  -> MVar Response
  -> ReqId
  -> ConsumerId
  -> Topic
  -> Subscription
  -> IO ()
newSubscriber (Conn s) var (ReqId req) (CId cid) topic (Subscription stype sname)
  = do
    logRequest $ P.subscribe req cid topic stype sname
    sendSimpleCmd s $ P.subscribe req cid topic stype sname
    readMVar var >>= logResponse

flow :: Connection -> ConsumerId -> Permits -> IO ()
flow (Conn s) (CId cid) (Permits p) = do
  logRequest $ P.flow cid p
  sendSimpleCmd s $ P.flow cid p

ack :: MonadIO m => Connection -> ConsumerId -> MessageIdData -> m ()
ack (Conn s) (CId cid) msgId = do
  logRequest $ P.ack cid msgId
  sendSimpleCmd s $ P.ack cid msgId

closeConsumer :: Connection -> MVar Response -> ConsumerId -> ReqId -> IO ()
closeConsumer (Conn s) var (CId cid) (ReqId req) = do
  logRequest $ P.closeConsumer req cid
  sendSimpleCmd s $ P.closeConsumer req cid
  readMVar var >>= logResponse

------ Keep Alive -------

ping :: (MonadThrow m, MonadIO m) => Connection -> Chan Response -> m ()
ping (Conn s) chan = do
  logRequest P.ping
  sendSimpleCmd s P.ping
  cmd <- getCommand <$> liftIO (readChan chan)
  case cmd ^. F.maybe'pong of
    Just p  -> logResponse p
    Nothing -> liftIO . throwIO $ userError "Failed to get PONG"

pong :: MonadIO m => Connection -> m ()
pong (Conn s) = do
  logRequest P.pong
  sendSimpleCmd s P.pong

------ Payload commands ------

send
  :: Connection
  -> MVar Response
  -> ProducerId
  -> SeqId
  -> PulsarMessage
  -> IO ()
send (Conn s) var (PId pid) (SeqId sid) (PulsarMessage msg) = do
  logRequest $ P.send pid sid
  sendPayloadCmd s (P.send pid sid) P.messageMetadata (Just $ Payload msg)
  readMVar var >>= logResponse