/src/AMQPEvents.hs

http://github.com/biilmann/eventsource-broker · Haskell · 98 lines · 69 code · 20 blank · 9 comment · 2 complexity · 12dfe6f8d0be6e2a7c668c1273db04bf MD5 · raw file

  1. {-# LANGUAGE OverloadedStrings #-}
  2. module AMQPEvents
  3. (
  4. AMQPEvent(..)
  5. , Channel
  6. , openEventChannel
  7. , publishEvent
  8. ) where
  9. import Control.Applicative((<$>), (<*>))
  10. import Control.Monad(mzero)
  11. import Control.Monad.Fix(fix)
  12. import Control.Concurrent(forkIO)
  13. import Control.Concurrent.Chan(Chan, newChan, readChan, writeChan)
  14. import Data.Aeson(FromJSON(..), ToJSON(..), Value(..), Result(..), fromJSON, toJSON, object, json, encode, (.:), (.:?), (.=))
  15. import Data.Attoparsec(parse, maybeResult)
  16. import qualified Data.ByteString as B
  17. import qualified Data.ByteString.Lazy as LB
  18. import Data.Maybe(fromJust, fromMaybe)
  19. import Data.String.Utils(split)
  20. import Text.URI(URI(..), parseURI)
  21. import System.Posix.Env(getEnvDefault)
  22. import Network.AMQP
  23. -- |Wraps a AMQPChannel to publish on and a listerner chan to read from
  24. type AMQPConn = (Channel, Chan AMQPEvent)
  25. -- |The AMQPEvent represents and incomming message that should be
  26. -- mapped to an EventSource event.
  27. data AMQPEvent = AMQPEvent
  28. { amqpChannel :: B.ByteString
  29. , amqpUser :: B.ByteString
  30. , amqpData :: B.ByteString
  31. , amqpId :: Maybe B.ByteString
  32. , amqpName :: Maybe B.ByteString
  33. }
  34. instance FromJSON AMQPEvent where
  35. parseJSON (Object v) = AMQPEvent <$>
  36. v .: "channel" <*>
  37. v .: "user" <*>
  38. v .: "data" <*>
  39. v .:? "id" <*>
  40. v .:? "name"
  41. parseJSON _ = mzero
  42. instance ToJSON AMQPEvent where
  43. toJSON (AMQPEvent c u d i n) = object ["channel" .= c, "user" .= u, "data" .= d, "id" .= i, "name" .= n]
  44. exchange = "eventsource.fanout"
  45. -- |Connects to an AMQP broker.
  46. -- Tries to get credentials, host and vhost from the AMQP_URL
  47. -- environment variable
  48. -- Take an exchange name and a queue name
  49. openEventChannel :: String -> IO AMQPConn
  50. openEventChannel queue = do
  51. amqpURI <- getEnvDefault "AMQP_URL" "amqp://guest:guest@127.0.0.1/"
  52. let uri = fromJust $ parseURI amqpURI
  53. let auth = fromMaybe "guest:guest" $ uriUserInfo uri
  54. let host = fromMaybe "127.0.0.1" $ uriRegName uri
  55. let vhost = uriPath uri
  56. let [user,password] = split ":" auth
  57. conn <- openConnection host vhost user password
  58. chan <- openChannel conn
  59. declareQueue chan newQueue {queueName = queue, queueAutoDelete = True, queueDurable = False}
  60. declareExchange chan newExchange {exchangeName = exchange, exchangeType = "fanout", exchangeDurable = False}
  61. bindQueue chan queue exchange queue
  62. listener <- newChan
  63. forkIO $ fix $ \loop -> readChan listener >> loop
  64. consumeMsgs chan queue NoAck (sendTo listener)
  65. return (chan, listener)
  66. publishEvent chan queue event =
  67. publishMsg chan exchange queue
  68. newMsg {msgBody = encode event}
  69. -- |Write messages from AMQP to a channel
  70. sendTo :: Chan AMQPEvent -> (Message, Envelope) -> IO ()
  71. sendTo chan (msg, _) =
  72. case maybeResult $ parse json (B.concat $ LB.toChunks (msgBody msg)) of
  73. Just value -> case fromJSON value of
  74. Success event -> do
  75. writeChan chan event
  76. Error _ -> do
  77. return ()
  78. Nothing -> return ()