packages feed

haskell-pgmq-0.1.0.0: src/Database/PGMQ/Types.hs

{-|
Module      : Database.PGMQ.Types
Description : PGMQ types
Copyright   : (c) Gargantext, 2024-Present
License     : AGPL
Maintainer  : gargantext@iscpif.fr
Stability   : experimental
Portability : POSIX
-}

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
    
module Database.PGMQ.Types
where

import Data.Aeson (FromJSON, ToJSON)
import Data.Time.LocalTime (ZonedTime, zonedTimeToUTC)
import Data.Typeable (Typeable)
import Database.PostgreSQL.Simple qualified as PSQL
import Database.PostgreSQL.Simple.FromRow qualified as PSQL (field, FromRow(..))
import Database.PostgreSQL.Simple.Newtypes qualified as PSQL (getAeson)


-- | Time in seconds before the message becomes visible (used when
-- | sending message to pgmq).
type Delay = Int  -- in seconds
-- | Number of messages in metrics report
type MessageCount = Int
-- | The ID of a message in pgmq
type MessageId = Int
-- | Name of queue in pgmq
type Queue = String
-- | Number of times a message was read from pgmq
type ReadCount = Int
-- | https://tembo.io/pgmq/#visibility-timeout-vt
--   (in seconds)
--   pgmq guarantees exactly once delivery of a message within a
--   visibility timeout. The visibility timeout is the amount of time a
--   message is invisible to other consumers after it has been read by
--   a consumer. If the message is NOT deleted or archived within the
--   visibility timeout, it will become visible again and can be read
--   by another consumer.
type VisibilityTimeout = Int

-- | 'MaxPollSeconds' and 'PollIntervalMs' are used in 'pgmq.read_with_poll'
type MaxPollSeconds = Int
type PollIntervalMs = Int

    
-- | Basic message typeclass for PGMQ: it has to be jsonb-serializable
type SerializableMessage a = ( FromJSON a
                             , ToJSON a
                             -- NOTE This shouldn't be necessary
                             , Typeable a )

-- | Message, as returned by the 'pgmq.read' function
data Message a =
  Message { msgId :: MessageId
          , readCt :: ReadCount
          , enqueuedAt :: ZonedTime
          , archivedAt :: Maybe ZonedTime
          , vt :: ZonedTime
          , message :: a }
  deriving (Show)
-- NOTE I'm not sure if this is needed
instance Eq a => Eq (Message a) where
  (==) msg1 msg2 =
    (msgId msg1) == (msgId msg2) &&
    (readCt msg1) == (readCt msg2) &&
    (zonedTimeToUTC $ vt msg1) == (zonedTimeToUTC $ vt msg2) &&
    (message msg1) == (message msg2)
instance SerializableMessage a => PSQL.FromRow (Message a) where
  fromRow = do
    msgId <- PSQL.field
    readCt <- PSQL.field
    enqueuedAt <- PSQL.field
    archivedAt <- PSQL.field
    vt <- PSQL.field
    messageA <- PSQL.field

    return $ Message { message = PSQL.getAeson messageA, .. }

-- | Returned by pgmq.list_queues
data QueueInfo =
  QueueInfo { queueName :: Queue
            , isPartitioned :: Bool
            , isUnlogged :: Bool
            , createdAt :: ZonedTime }
  deriving (Show)
instance PSQL.FromRow QueueInfo where
  fromRow = do
    queueName <- PSQL.field
    isPartitioned <- PSQL.field
    isUnlogged <- PSQL.field
    createdAt <- PSQL.field

    return $ QueueInfo { .. }


-- | Returned by pgmq.metrics, pgmq.metrics_all
data Metrics =
  Metrics { queueName :: Queue
          , queueLength :: Int
          , newestMsgAgeSec :: Maybe Int
          , oldestMsgAgeSec :: Maybe Int
          , totalMessages :: MessageCount
          , scrapeTime :: ZonedTime  }
  deriving (Show)
instance PSQL.FromRow Metrics where
  fromRow = do
    queueName <- PSQL.field
    queueLength <- PSQL.field
    newestMsgAgeSec <- PSQL.field
    oldestMsgAgeSec <- PSQL.field
    totalMessages <- PSQL.field
    scrapeTime <- PSQL.field

    return $ Metrics { .. }