@@ -15,6 +15,7 @@ import Data.Time.Clock (UTCTime)
15
15
import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds , posixSecondsToUTCTime )
16
16
import Control.Concurrent.AlarmClock (newAlarmClock , setAlarm )
17
17
import qualified Hasql.Notifications as H
18
+ import qualified Hasql.Pool as H
18
19
import qualified Network.Wai as Wai
19
20
import qualified Network.Wai.Handler.WebSockets as WS
20
21
import qualified Network.WebSockets as WS
@@ -31,17 +32,25 @@ import PostgresWebsockets.Context ( Context(..) )
31
32
import PostgresWebsockets.Config (AppConfig (.. ))
32
33
import qualified PostgresWebsockets.Broadcast as B
33
34
35
+
36
+ data Event =
37
+ WebsocketMessage
38
+ | ServerStateChange
39
+ deriving (Show , Eq , Generic )
40
+
34
41
data Message = Message
35
42
{ claims :: A. Object
36
- , channel :: Text
43
+ , event :: Event
37
44
, payload :: Text
45
+ , channel :: Text
38
46
} deriving (Show , Eq , Generic )
39
47
48
+ instance A. ToJSON Event
40
49
instance A. ToJSON Message
41
50
42
51
-- | Given a secret, a function to fetch the system time, a Hasql Pool and a Multiplexer this will give you a WAI middleware.
43
52
postgresWsMiddleware :: Context -> Wai. Middleware
44
- postgresWsMiddleware =
53
+ postgresWsMiddleware =
45
54
WS. websocketsOr WS. defaultConnectionOptions . wsApp
46
55
47
56
-- private functions
@@ -85,15 +94,15 @@ wsApp Context{..} pendingConn =
85
94
Just _ -> pure ()
86
95
Nothing -> pure ()
87
96
88
- let sendNotification =
89
- relayChannelData
90
- (void . H. notifyPool ctxPool (configListenChannel ctxConfig) . toS)
91
- validClaims
92
- ctxGetTime
97
+ let sendNotification msg channel = sendMessageWithTimestamp $ websocketMessageForChannel msg channel
98
+ sendMessageToDatabase = sendToDatabase ctxPool (configListenChannel ctxConfig)
99
+ sendMessageWithTimestamp = timestampMessage ctxGetTime >=> sendMessageToDatabase
100
+ websocketMessageForChannel = Message validClaims WebsocketMessage
101
+ serverStateChangeMessage = Message validClaims ServerStateChange
93
102
94
103
case configMetaChannel ctxConfig of
95
104
Nothing -> pure ()
96
- Just ch -> sendNotification " Connecion Open" ch
105
+ Just ch -> sendMessageWithTimestamp $ serverStateChangeMessage " Connecion Open" ch
97
106
98
107
when (hasRead mode) $
99
108
forM_ chs $ flip (onMessage ctxMulti) $ WS. sendTextData conn . B. payload
@@ -107,25 +116,22 @@ wsApp Context{..} pendingConn =
107
116
-- Having both channel and claims as parameters seem redundant
108
117
-- But it allows the function to ignore the claims structure and the source
109
118
-- of the channel, so all claims decoding can be coded in the caller
110
- notifySession :: WS. Connection -> (ByteString -> Text -> IO () ) -> [ByteString ] -> IO ()
119
+ notifySession :: WS. Connection -> (Text -> Text -> IO () ) -> [ByteString ] -> IO ()
111
120
notifySession wsCon sendToChannel chs =
112
121
withAsync (forever relayData) wait
113
122
where
114
- relayData = do
123
+ relayData = do
115
124
msg <- WS. receiveData wsCon
116
125
forM_ chs (sendToChannel msg . toS)
117
126
118
- relayChannelData :: ( ByteString -> IO () ) -> A. Object -> IO UTCTime -> ByteString -> Text -> IO ()
119
- relayChannelData send claimsToSend getTime msg ch =
120
- claimsWithTime >>= (send . jsonMsg)
127
+ sendToDatabase :: H. Pool -> Text -> Message -> IO ()
128
+ sendToDatabase pool dbChannel =
129
+ notify . jsonMsg
121
130
where
122
- -- we need to decode the bytestring to re-encode valid JSON for the notification
123
- jsonMsg :: M. HashMap Text A. Value -> ByteString
124
- jsonMsg cl = BL. toStrict . A. encode . Message cl ch . decodeUtf8With T. lenientDecode $ msg
125
-
126
- claimsWithTime :: IO (M. HashMap Text A. Value )
127
- claimsWithTime = do
128
- time <- utcTimeToPOSIXSeconds <$> getTime
129
- return $ M. insert " message_delivered_at" (A. Number $ realToFrac time) claimsWithChannel
131
+ notify = void . H. notifyPool pool dbChannel . toS
132
+ jsonMsg = BL. toStrict . A. encode
130
133
131
- claimsWithChannel = M. insert " channel" (A. String ch) claimsToSend
134
+ timestampMessage :: IO UTCTime -> Message -> IO Message
135
+ timestampMessage getTime msg@ Message {.. } = do
136
+ time <- utcTimeToPOSIXSeconds <$> getTime
137
+ return $ msg{ claims = M. insert " message_delivered_at" (A. Number $ realToFrac time) claims}
0 commit comments