module Docs.Hasql.Sessions
    ( createDocument
    , getDocument
    , getDocuments
    , getDocumentsBy
    , createTextElement
    , createTextRevision
    , getTextElementRevision
    , createTreeRevision
    , getTreeRevision
    , getTree
    , getTextRevisionHistory
    , getTreeRevisionHistory
    , getDocumentRevisionHistory
    , existsDocument
    , existsTextElement
    , existsTextRevision
    , existsTreeRevision
    , hasPermission
    , isGroupAdmin
    , getComments
    , logMessage
    , getLogs
    , getRevisionKey
    ) where

import Data.Functor ((<&>))
import Data.Text (Text)
import Data.Time (UTCTime)
import Data.Vector (Vector)
import qualified Data.Vector as Vector

import Hasql.Session (Session, statement)
import Hasql.Transaction.Sessions
    ( IsolationLevel (..)
    , Mode (..)
    , transaction
    )

import UserManagement.DocumentPermission (Permission)
import UserManagement.Group (GroupID)
import UserManagement.User (UserID)

import Data.Aeson (ToJSON)
import Docs.Comment (Comment, CommentAnchor, CommentRef (CommentRef), Message)
import qualified Docs.Comment as Comment
import Docs.Document (Document, DocumentID)
import Docs.DocumentHistory (DocumentHistory (..))
import Docs.Hash (Hash)
import qualified Docs.Hasql.Statements as Statements
import qualified Docs.Hasql.Transactions as Transactions
import Docs.Revision (RevisionKey, RevisionRef (RevisionRef))
import Docs.TextElement
    ( TextElement
    , TextElementID
    , TextElementKind
    , TextElementRef (TextElementRef)
    , TextElementType
    )
import Docs.TextRevision
    ( TextElementRevision
    , TextRevision
    , TextRevisionHistory (TextRevisionHistory)
    , TextRevisionRef
    )
import Docs.Tree (Node)
import Docs.TreeRevision
    ( TreeRevision
    , TreeRevisionHistory (TreeRevisionHistory)
    , TreeRevisionRef (..)
    )
import GHC.Int (Int64)
import Logging.Logs (LogMessage, Scope, Severity)

existsDocument :: DocumentID -> Session Bool
existsDocument :: DocumentID -> Session Bool
existsDocument = (DocumentID -> Statement DocumentID Bool -> Session Bool)
-> Statement DocumentID Bool -> DocumentID -> Session Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip DocumentID -> Statement DocumentID Bool -> Session Bool
forall params result.
params -> Statement params result -> Session result
statement Statement DocumentID Bool
Statements.existsDocument

existsTextElement :: TextElementRef -> Session Bool
existsTextElement :: TextElementRef -> Session Bool
existsTextElement = (TextElementRef -> Statement TextElementRef Bool -> Session Bool)
-> Statement TextElementRef Bool -> TextElementRef -> Session Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip TextElementRef -> Statement TextElementRef Bool -> Session Bool
forall params result.
params -> Statement params result -> Session result
statement Statement TextElementRef Bool
Statements.existsTextElement

existsTextRevision :: TextRevisionRef -> Session Bool
existsTextRevision :: TextRevisionRef -> Session Bool
existsTextRevision = (TextRevisionRef -> Statement TextRevisionRef Bool -> Session Bool)
-> Statement TextRevisionRef Bool
-> TextRevisionRef
-> Session Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip TextRevisionRef -> Statement TextRevisionRef Bool -> Session Bool
forall params result.
params -> Statement params result -> Session result
statement Statement TextRevisionRef Bool
Statements.existsTextRevision

existsTreeRevision :: TreeRevisionRef -> Session Bool
existsTreeRevision :: TreeRevisionRef -> Session Bool
existsTreeRevision = (TreeRevisionRef -> Statement TreeRevisionRef Bool -> Session Bool)
-> Statement TreeRevisionRef Bool
-> TreeRevisionRef
-> Session Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip TreeRevisionRef -> Statement TreeRevisionRef Bool -> Session Bool
forall params result.
params -> Statement params result -> Session result
statement Statement TreeRevisionRef Bool
Statements.existsTreeRevision

createDocument :: Text -> GroupID -> UserID -> Session Document
createDocument :: Text -> GroupID -> UserID -> Session Document
createDocument Text
name GroupID
group UserID
user =
    (Text, GroupID, UserID)
-> Statement (Text, GroupID, UserID) Document -> Session Document
forall params result.
params -> Statement params result -> Session result
statement (Text
name, GroupID
group, UserID
user) Statement (Text, GroupID, UserID) Document
Statements.createDocument

getDocument :: DocumentID -> Session (Maybe Document)
getDocument :: DocumentID -> Session (Maybe Document)
getDocument = (DocumentID
-> Statement DocumentID (Maybe Document)
-> Session (Maybe Document)
forall params result.
params -> Statement params result -> Session result
`statement` Statement DocumentID (Maybe Document)
Statements.getDocument)

getDocuments :: UserID -> Session (Vector Document)
getDocuments :: UserID -> Session (Vector Document)
getDocuments = (UserID
-> Statement UserID (Vector Document) -> Session (Vector Document)
forall params result.
params -> Statement params result -> Session result
`statement` Statement UserID (Vector Document)
Statements.getDocuments)

getDocumentsBy :: Maybe UserID -> Maybe GroupID -> Session (Vector Document)
getDocumentsBy :: Maybe UserID -> Maybe GroupID -> Session (Vector Document)
getDocumentsBy = ((Maybe UserID, Maybe GroupID) -> Session (Vector Document))
-> Maybe UserID -> Maybe GroupID -> Session (Vector Document)
forall a b c. ((a, b) -> c) -> a -> b -> c
curry ((Maybe UserID, Maybe GroupID)
-> Statement (Maybe UserID, Maybe GroupID) (Vector Document)
-> Session (Vector Document)
forall params result.
params -> Statement params result -> Session result
`statement` Statement (Maybe UserID, Maybe GroupID) (Vector Document)
Statements.getDocumentsBy)

createTextElement
    :: DocumentID
    -> TextElementKind
    -> TextElementType
    -> Session TextElement
createTextElement :: DocumentID -> Text -> Text -> Session TextElement
createTextElement DocumentID
docID Text
kind Text
type_ = (DocumentID, Text, Text)
-> Statement (DocumentID, Text, Text) TextElement
-> Session TextElement
forall params result.
params -> Statement params result -> Session result
statement (DocumentID
docID, Text
kind, Text
type_) Statement (DocumentID, Text, Text) TextElement
Statements.createTextElement

createTextRevision
    :: UserID
    -> TextElementRef
    -> Text
    -> Vector CommentAnchor
    -> Session TextRevision
createTextRevision :: UserID
-> TextElementRef
-> Text
-> Vector CommentAnchor
-> Session TextRevision
createTextRevision =
    (((IsolationLevel
-> Mode -> Transaction TextRevision -> Session TextRevision
forall a. IsolationLevel -> Mode -> Transaction a -> Session a
transaction IsolationLevel
Serializable Mode
Write (Transaction TextRevision -> Session TextRevision)
-> (Vector CommentAnchor -> Transaction TextRevision)
-> Vector CommentAnchor
-> Session TextRevision
forall b c a. (b -> c) -> (a -> b) -> a -> c
.) ((Vector CommentAnchor -> Transaction TextRevision)
 -> Vector CommentAnchor -> Session TextRevision)
-> (Text -> Vector CommentAnchor -> Transaction TextRevision)
-> Text
-> Vector CommentAnchor
-> Session TextRevision
forall b c a. (b -> c) -> (a -> b) -> a -> c
.) ((Text -> Vector CommentAnchor -> Transaction TextRevision)
 -> Text -> Vector CommentAnchor -> Session TextRevision)
-> (TextElementRef
    -> Text -> Vector CommentAnchor -> Transaction TextRevision)
-> TextElementRef
-> Text
-> Vector CommentAnchor
-> Session TextRevision
forall b c a. (b -> c) -> (a -> b) -> a -> c
.) ((TextElementRef
  -> Text -> Vector CommentAnchor -> Transaction TextRevision)
 -> TextElementRef
 -> Text
 -> Vector CommentAnchor
 -> Session TextRevision)
-> (UserID
    -> TextElementRef
    -> Text
    -> Vector CommentAnchor
    -> Transaction TextRevision)
-> UserID
-> TextElementRef
-> Text
-> Vector CommentAnchor
-> Session TextRevision
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UserID
-> TextElementRef
-> Text
-> Vector CommentAnchor
-> Transaction TextRevision
Transactions.createTextRevision

getTextElementRevision
    :: TextRevisionRef
    -> Session (Maybe TextElementRevision)
getTextElementRevision :: TextRevisionRef -> Session (Maybe TextElementRevision)
getTextElementRevision TextRevisionRef
ref = do
    (TextRevisionID -> Session (Vector CommentAnchor))
-> Session (Maybe TextElementRevision)
textElementRevision <- TextRevisionRef
-> Statement
     TextRevisionRef
     ((TextRevisionID -> Session (Vector CommentAnchor))
      -> Session (Maybe TextElementRevision))
-> Session
     ((TextRevisionID -> Session (Vector CommentAnchor))
      -> Session (Maybe TextElementRevision))
forall params result.
params -> Statement params result -> Session result
statement TextRevisionRef
ref Statement
  TextRevisionRef
  ((TextRevisionID -> Session (Vector CommentAnchor))
   -> Session (Maybe TextElementRevision))
forall (m :: * -> *).
Monad m =>
Statement
  TextRevisionRef
  ((TextRevisionID -> m (Vector CommentAnchor))
   -> m (Maybe TextElementRevision))
Statements.getTextElementRevision
    (TextRevisionID -> Session (Vector CommentAnchor))
-> Session (Maybe TextElementRevision)
textElementRevision ((TextRevisionID -> Session (Vector CommentAnchor))
 -> Session (Maybe TextElementRevision))
-> (TextRevisionID -> Session (Vector CommentAnchor))
-> Session (Maybe TextElementRevision)
forall a b. (a -> b) -> a -> b
$ (TextRevisionID
 -> Statement TextRevisionID (Vector CommentAnchor)
 -> Session (Vector CommentAnchor))
-> Statement TextRevisionID (Vector CommentAnchor)
-> TextRevisionID
-> Session (Vector CommentAnchor)
forall a b c. (a -> b -> c) -> b -> a -> c
flip TextRevisionID
-> Statement TextRevisionID (Vector CommentAnchor)
-> Session (Vector CommentAnchor)
forall params result.
params -> Statement params result -> Session result
statement Statement TextRevisionID (Vector CommentAnchor)
Statements.getCommentAnchors

createTreeRevision
    :: UserID
    -> DocumentID
    -> Node TextElementID
    -> Session (TreeRevision TextElementID)
createTreeRevision :: UserID
-> DocumentID
-> Node TextElementID
-> Session (TreeRevision TextElementID)
createTreeRevision UserID
authorID DocumentID
docID Node TextElementID
rootNode =
    IsolationLevel
-> Mode
-> Transaction (TreeRevision TextElementID)
-> Session (TreeRevision TextElementID)
forall a. IsolationLevel -> Mode -> Transaction a -> Session a
transaction
        IsolationLevel
Serializable
        Mode
Write
        (Transaction (TreeRevision TextElementID)
 -> Session (TreeRevision TextElementID))
-> Transaction (TreeRevision TextElementID)
-> Session (TreeRevision TextElementID)
forall a b. (a -> b) -> a -> b
$ UserID
-> DocumentID
-> Node TextElementID
-> Transaction (TreeRevision TextElementID)
Transactions.createTreeRevision UserID
authorID DocumentID
docID Node TextElementID
rootNode

getTreeRevision
    :: TreeRevisionRef
    -> Session (Maybe (TreeRevision TextElement))
getTreeRevision :: TreeRevisionRef -> Session (Maybe (TreeRevision TextElement))
getTreeRevision =
    IsolationLevel
-> Mode
-> Transaction (Maybe (TreeRevision TextElement))
-> Session (Maybe (TreeRevision TextElement))
forall a. IsolationLevel -> Mode -> Transaction a -> Session a
transaction
        IsolationLevel
Serializable
        Mode
Write
        (Transaction (Maybe (TreeRevision TextElement))
 -> Session (Maybe (TreeRevision TextElement)))
-> (TreeRevisionRef
    -> Transaction (Maybe (TreeRevision TextElement)))
-> TreeRevisionRef
-> Session (Maybe (TreeRevision TextElement))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TreeRevisionRef -> Transaction (Maybe (TreeRevision TextElement))
Transactions.getTreeRevision

getTree :: Hash -> Session (Node TextElement)
getTree :: Hash -> Session (Node TextElement)
getTree =
    IsolationLevel
-> Mode
-> Transaction (Node TextElement)
-> Session (Node TextElement)
forall a. IsolationLevel -> Mode -> Transaction a -> Session a
transaction
        IsolationLevel
Serializable
        Mode
Write
        (Transaction (Node TextElement) -> Session (Node TextElement))
-> (Hash -> Transaction (Node TextElement))
-> Hash
-> Session (Node TextElement)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Hash -> Transaction (Node TextElement)
Transactions.getTree

getTextRevisionHistory
    :: TextElementRef
    -> Maybe UTCTime
    -> Maybe UTCTime
    -> Int64
    -> Session TextRevisionHistory
getTextRevisionHistory :: TextElementRef
-> Maybe UTCTime
-> Maybe UTCTime
-> GroupID
-> Session TextRevisionHistory
getTextRevisionHistory TextElementRef
ref Maybe UTCTime
after Maybe UTCTime
before GroupID
limit =
    (TextElementRef, Maybe UTCTime, Maybe UTCTime, GroupID)
-> Statement
     (TextElementRef, Maybe UTCTime, Maybe UTCTime, GroupID)
     (Vector TextRevisionHeader)
-> Session (Vector TextRevisionHeader)
forall params result.
params -> Statement params result -> Session result
statement (TextElementRef
ref, Maybe UTCTime
after, Maybe UTCTime
before, GroupID
limit) Statement
  (TextElementRef, Maybe UTCTime, Maybe UTCTime, GroupID)
  (Vector TextRevisionHeader)
Statements.getTextRevisionHistory
        Session (Vector TextRevisionHeader)
-> (Vector TextRevisionHeader -> TextRevisionHistory)
-> Session TextRevisionHistory
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> TextElementRef -> [TextRevisionHeader] -> TextRevisionHistory
TextRevisionHistory TextElementRef
ref ([TextRevisionHeader] -> TextRevisionHistory)
-> (Vector TextRevisionHeader -> [TextRevisionHeader])
-> Vector TextRevisionHeader
-> TextRevisionHistory
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector TextRevisionHeader -> [TextRevisionHeader]
forall a. Vector a -> [a]
Vector.toList

getTreeRevisionHistory
    :: DocumentID -> Maybe UTCTime -> Int64 -> Session TreeRevisionHistory
getTreeRevisionHistory :: DocumentID
-> Maybe UTCTime -> GroupID -> Session TreeRevisionHistory
getTreeRevisionHistory DocumentID
id_ Maybe UTCTime
before GroupID
limit =
    (DocumentID, Maybe UTCTime, GroupID)
-> Statement
     (DocumentID, Maybe UTCTime, GroupID) (Vector TreeRevisionHeader)
-> Session (Vector TreeRevisionHeader)
forall params result.
params -> Statement params result -> Session result
statement (DocumentID
id_, Maybe UTCTime
before, GroupID
limit) Statement
  (DocumentID, Maybe UTCTime, GroupID) (Vector TreeRevisionHeader)
Statements.getTreeRevisionHistory
        Session (Vector TreeRevisionHeader)
-> (Vector TreeRevisionHeader -> TreeRevisionHistory)
-> Session TreeRevisionHistory
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> DocumentID -> [TreeRevisionHeader] -> TreeRevisionHistory
TreeRevisionHistory DocumentID
id_ ([TreeRevisionHeader] -> TreeRevisionHistory)
-> (Vector TreeRevisionHeader -> [TreeRevisionHeader])
-> Vector TreeRevisionHeader
-> TreeRevisionHistory
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector TreeRevisionHeader -> [TreeRevisionHeader]
forall a. Vector a -> [a]
Vector.toList

getDocumentRevisionHistory
    :: DocumentID -> Maybe UTCTime -> Int64 -> Session DocumentHistory
getDocumentRevisionHistory :: DocumentID -> Maybe UTCTime -> GroupID -> Session DocumentHistory
getDocumentRevisionHistory DocumentID
id_ Maybe UTCTime
before GroupID
limit =
    (DocumentID, Maybe UTCTime, GroupID)
-> Statement
     (DocumentID, Maybe UTCTime, GroupID) (Vector DocumentHistoryItem)
-> Session (Vector DocumentHistoryItem)
forall params result.
params -> Statement params result -> Session result
statement (DocumentID
id_, Maybe UTCTime
before, GroupID
limit) Statement
  (DocumentID, Maybe UTCTime, GroupID) (Vector DocumentHistoryItem)
Statements.getDocumentRevisionHistory
        Session (Vector DocumentHistoryItem)
-> (Vector DocumentHistoryItem -> DocumentHistory)
-> Session DocumentHistory
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> DocumentID -> [DocumentHistoryItem] -> DocumentHistory
DocumentHistory DocumentID
id_ ([DocumentHistoryItem] -> DocumentHistory)
-> (Vector DocumentHistoryItem -> [DocumentHistoryItem])
-> Vector DocumentHistoryItem
-> DocumentHistory
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector DocumentHistoryItem -> [DocumentHistoryItem]
forall a. Vector a -> [a]
Vector.toList

hasPermission :: UserID -> DocumentID -> Permission -> Session Bool
hasPermission :: UserID -> DocumentID -> Permission -> Session Bool
hasPermission UserID
userID DocumentID
docID Permission
perms =
    (UserID, DocumentID, Permission)
-> Statement (UserID, DocumentID, Permission) Bool -> Session Bool
forall params result.
params -> Statement params result -> Session result
statement (UserID
userID, DocumentID
docID, Permission
perms) Statement (UserID, DocumentID, Permission) Bool
Statements.hasPermission

isGroupAdmin :: UserID -> GroupID -> Session Bool
isGroupAdmin :: UserID -> GroupID -> Session Bool
isGroupAdmin UserID
userID GroupID
groupID =
    (UserID, GroupID)
-> Statement (UserID, GroupID) Bool -> Session Bool
forall params result.
params -> Statement params result -> Session result
statement (UserID
userID, GroupID
groupID) Statement (UserID, GroupID) Bool
Statements.isGroupAdmin

getComments :: TextElementRef -> Session (Vector Comment)
getComments :: TextElementRef -> Session (Vector Comment)
getComments textRef :: TextElementRef
textRef@(TextElementRef DocumentID
docID TextElementID
textID) = do
    Vector Comment
comments <- (DocumentID, TextElementID)
-> Statement (DocumentID, TextElementID) (Vector Comment)
-> Session (Vector Comment)
forall params result.
params -> Statement params result -> Session result
statement (DocumentID
docID, TextElementID
textID) Statement (DocumentID, TextElementID) (Vector Comment)
Statements.getComments
    (Comment -> Session Comment)
-> Vector Comment -> Session (Vector Comment)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Vector a -> m (Vector b)
mapM Comment -> Session Comment
mapper Vector Comment
comments
  where
    mapper :: Comment -> Session Comment
    mapper :: Comment -> Session Comment
mapper Comment
comment = do
        Vector Message
replies <- CommentRef -> Session (Vector Message)
getReplies (CommentRef -> Session (Vector Message))
-> CommentRef -> Session (Vector Message)
forall a b. (a -> b) -> a -> b
$ TextElementRef -> CommentID -> CommentRef
CommentRef TextElementRef
textRef (CommentID -> CommentRef) -> CommentID -> CommentRef
forall a b. (a -> b) -> a -> b
$ Comment -> CommentID
Comment.identifier Comment
comment
        Comment -> Session Comment
forall a. a -> Session a
forall (m :: * -> *) a. Monad m => a -> m a
return (Comment -> Session Comment) -> Comment -> Session Comment
forall a b. (a -> b) -> a -> b
$ Comment
comment {Comment.replies = replies}
    getReplies :: CommentRef -> Session (Vector Message)
    getReplies :: CommentRef -> Session (Vector Message)
getReplies = (CommentRef
 -> Statement CommentRef (Vector Message)
 -> Session (Vector Message))
-> Statement CommentRef (Vector Message)
-> CommentRef
-> Session (Vector Message)
forall a b c. (a -> b -> c) -> b -> a -> c
flip CommentRef
-> Statement CommentRef (Vector Message)
-> Session (Vector Message)
forall params result.
params -> Statement params result -> Session result
statement Statement CommentRef (Vector Message)
Statements.getReplies

logMessage
    :: (ToJSON v)
    => Severity
    -- ^ severity of the log message
    -> Maybe UserID
    -- ^ source user
    -> Scope
    -- ^ scope (e.g, "docs.text.revision")
    -> v
    -- ^ content (json)
    -> Session LogMessage
    -- ^ created log message
logMessage :: forall v.
ToJSON v =>
Severity -> Maybe UserID -> Scope -> v -> Session LogMessage
logMessage Severity
severity Maybe UserID
source Scope
scope v
content =
    (Severity, Maybe UserID, Scope, v)
-> Statement (Severity, Maybe UserID, Scope, v) LogMessage
-> Session LogMessage
forall params result.
params -> Statement params result -> Session result
statement (Severity
severity, Maybe UserID
source, Scope
scope, v
content) Statement (Severity, Maybe UserID, Scope, v) LogMessage
forall v.
ToJSON v =>
Statement (Severity, Maybe UserID, Scope, v) LogMessage
Statements.logMessage

getLogs
    :: Maybe UTCTime
    -- ^ offset
    -> Int64
    -- ^ limit
    -> Session (Vector LogMessage)
    -- ^ log messages
getLogs :: Maybe UTCTime -> GroupID -> Session (Vector LogMessage)
getLogs = ((Maybe UTCTime, GroupID) -> Session (Vector LogMessage))
-> Maybe UTCTime -> GroupID -> Session (Vector LogMessage)
forall a b c. ((a, b) -> c) -> a -> b -> c
curry ((Maybe UTCTime, GroupID)
-> Statement (Maybe UTCTime, GroupID) (Vector LogMessage)
-> Session (Vector LogMessage)
forall params result.
params -> Statement params result -> Session result
`statement` Statement (Maybe UTCTime, GroupID) (Vector LogMessage)
Statements.getLogs)

getRevisionKey :: RevisionRef -> Session (Maybe RevisionKey)
getRevisionKey :: RevisionRef -> Session (Maybe RevisionKey)
getRevisionKey (RevisionRef DocumentID
docID RevisionSelector
revID) =
    (DocumentID, RevisionSelector)
-> Statement (DocumentID, RevisionSelector) (Maybe RevisionKey)
-> Session (Maybe RevisionKey)
forall params result.
params -> Statement params result -> Session result
statement (DocumentID
docID, RevisionSelector
revID) Statement (DocumentID, RevisionSelector) (Maybe RevisionKey)
Statements.getRevisionKey