{-# LANGUAGE TupleSections #-}

module Docs.Hasql.Transactions
    ( now
    , getTreeRevision
    , existsTreeRevision
    , getTree
    , createDocument
    , createTextElement
    , getTextElementRevision
    , existsTextRevision
    , updateTextRevision
    , createTextRevision
    , putTree
    , createTreeRevision
    , existsDocument
    , existsTextElement
    , getLatestTextRevisionID
    , isTextElementInDocument
    , hasPermission
    , isGroupAdmin
    , createComment
    , existsComment
    , resolveComment
    , createReply
    , logMessage
    , updateLatestTitle
    , getTextElement
    , createDraftTextRevision
    , getDraftTextRevision
    , deleteDraftTextRevision
    , getRevisionKey
    , getDocument
    , getDocuments
    , getDocumentsBy
    ) where

import qualified Crypto.Hash.SHA1 as SHA1
import Hasql.Transaction (Transaction, statement)

import Control.Monad (guard)
import Data.Functor ((<&>))
import qualified Data.Set as Set
import Data.Text (Text)
import Data.Time (UTCTime)
import Data.Vector (Vector)
import qualified Data.Vector as Vector

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

import Data.Aeson (ToJSON)
import Docs.Comment (Comment, CommentAnchor, CommentID, CommentRef, Message)
import qualified Docs.Comment as Comment
import Docs.Document (Document, DocumentID)
import Docs.Hash
    ( Hash (Hash)
    , Hashable (..)
    )
import qualified Docs.Hasql.Statements as Statements
import Docs.Hasql.TreeEdge
    ( TreeEdge (TreeEdge)
    , TreeEdgeChild (TreeEdgeToNode, TreeEdgeToTextElement)
    , TreeEdgeChildRef (..)
    )
import qualified Docs.Hasql.TreeEdge as TreeEdge
import Docs.Revision (RevisionKey, RevisionRef (RevisionRef))
import Docs.TextElement
    ( TextElement
    , TextElementID
    , TextElementKind
    , TextElementRef (..)
    , TextElementType
    )
import Docs.TextRevision
    ( DraftRevision
    , TextElementRevision
    , TextRevision
    , TextRevisionID
    , TextRevisionRef
    )
import Docs.Tree (Node (Node), NodeHeader, Tree (Leaf, Tree))
import qualified Docs.Tree as Tree
import Docs.TreeRevision (TreeRevision, TreeRevisionRef (TreeRevisionRef))
import qualified Docs.TreeRevision as TreeRevision
import Logging.Logs (LogMessage, Scope, Severity)

now :: Transaction UTCTime
now :: Transaction UTCTime
now = () -> Statement () UTCTime -> Transaction UTCTime
forall a b. a -> Statement a b -> Transaction b
statement () Statement () UTCTime
Statements.now

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

createDocument :: Text -> GroupID -> UserID -> Transaction Document
createDocument :: Text -> GroupID -> UserID -> Transaction Document
createDocument Text
name GroupID
group UserID
user =
    (Text, GroupID, UserID)
-> Statement (Text, GroupID, UserID) Document
-> Transaction Document
forall a b. a -> Statement a b -> Transaction b
statement (Text
name, GroupID
group, UserID
user) Statement (Text, GroupID, UserID) Document
Statements.createDocument

createTextElement
    :: DocumentID
    -> TextElementKind
    -> TextElementType
    -> Transaction TextElement
createTextElement :: DocumentID -> Text -> Text -> Transaction TextElement
createTextElement DocumentID
docID Text
kind Text
type_ = (DocumentID, Text, Text)
-> Statement (DocumentID, Text, Text) TextElement
-> Transaction TextElement
forall a b. a -> Statement a b -> Transaction b
statement (DocumentID
docID, Text
kind, Text
type_) Statement (DocumentID, Text, Text) TextElement
Statements.createTextElement

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

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

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

getLatestTextRevisionID :: TextElementRef -> Transaction (Maybe TextRevisionID)
getLatestTextRevisionID :: TextElementRef -> Transaction (Maybe TextRevisionID)
getLatestTextRevisionID = (TextElementRef
-> Statement TextElementRef (Maybe TextRevisionID)
-> Transaction (Maybe TextRevisionID)
forall a b. a -> Statement a b -> Transaction b
`statement` Statement TextElementRef (Maybe TextRevisionID)
Statements.getLatestTextRevisionID)

updateTextRevision
    :: TextRevisionID
    -> Text
    -> Vector CommentAnchor
    -> Transaction TextRevision
updateTextRevision :: TextRevisionID
-> Text -> Vector CommentAnchor -> Transaction TextRevision
updateTextRevision TextRevisionID
rev Text
text Vector CommentAnchor
commentAnchors = do
    (TextRevisionID, Vector CommentID)
-> Statement (TextRevisionID, Vector CommentID) ()
-> Transaction ()
forall a b. a -> Statement a b -> Transaction b
statement
        (TextRevisionID
rev, CommentAnchor -> CommentID
Comment.comment (CommentAnchor -> CommentID)
-> Vector CommentAnchor -> Vector CommentID
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Vector CommentAnchor
commentAnchors)
        Statement (TextRevisionID, Vector CommentID) ()
Statements.deleteCommentAnchorsExcept
    (TextRevisionID -> Transaction (Vector CommentAnchor))
-> Transaction TextRevision
textRevision <- (TextRevisionID, Text)
-> Statement
     (TextRevisionID, Text)
     ((TextRevisionID -> Transaction (Vector CommentAnchor))
      -> Transaction TextRevision)
-> Transaction
     ((TextRevisionID -> Transaction (Vector CommentAnchor))
      -> Transaction TextRevision)
forall a b. a -> Statement a b -> Transaction b
statement (TextRevisionID
rev, Text
text) Statement
  (TextRevisionID, Text)
  ((TextRevisionID -> Transaction (Vector CommentAnchor))
   -> Transaction TextRevision)
forall (m :: * -> *).
Monad m =>
Statement
  (TextRevisionID, Text)
  ((TextRevisionID -> m (Vector CommentAnchor)) -> m TextRevision)
Statements.updateTextRevision
    (TextRevisionID -> Transaction (Vector CommentAnchor))
-> Transaction TextRevision
textRevision ((TextRevisionID -> Transaction (Vector CommentAnchor))
 -> Transaction TextRevision)
-> (TextRevisionID -> Transaction (Vector CommentAnchor))
-> Transaction TextRevision
forall a b. (a -> b) -> a -> b
$
        \TextRevisionID
newRev ->
            ((TextRevisionID, CommentAnchor) -> Transaction CommentAnchor)
-> Vector (TextRevisionID, CommentAnchor)
-> Transaction (Vector CommentAnchor)
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 ((TextRevisionID, CommentAnchor)
-> Statement (TextRevisionID, CommentAnchor) CommentAnchor
-> Transaction CommentAnchor
forall a b. a -> Statement a b -> Transaction b
`statement` Statement (TextRevisionID, CommentAnchor) CommentAnchor
Statements.putCommentAnchor) ((TextRevisionID
newRev,) (CommentAnchor -> (TextRevisionID, CommentAnchor))
-> Vector CommentAnchor -> Vector (TextRevisionID, CommentAnchor)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Vector CommentAnchor
commentAnchors)

createTextRevision
    :: UserID
    -> TextElementRef
    -> Text
    -> Vector CommentAnchor
    -> Transaction TextRevision
createTextRevision :: UserID
-> TextElementRef
-> Text
-> Vector CommentAnchor
-> Transaction TextRevision
createTextRevision UserID
userID (TextElementRef DocumentID
_ TextElementID
textID) Text
content Vector CommentAnchor
commentAnchors = do
    (TextRevisionID -> Transaction (Vector CommentAnchor))
-> Transaction TextRevision
textRevision <-
        (TextElementID, UserID, Text)
-> Statement
     (TextElementID, UserID, Text)
     ((TextRevisionID -> Transaction (Vector CommentAnchor))
      -> Transaction TextRevision)
-> Transaction
     ((TextRevisionID -> Transaction (Vector CommentAnchor))
      -> Transaction TextRevision)
forall a b. a -> Statement a b -> Transaction b
statement (TextElementID
textID, UserID
userID, Text
content) Statement
  (TextElementID, UserID, Text)
  ((TextRevisionID -> Transaction (Vector CommentAnchor))
   -> Transaction TextRevision)
forall (m :: * -> *).
Monad m =>
Statement
  (TextElementID, UserID, Text)
  ((TextRevisionID -> m (Vector CommentAnchor)) -> m TextRevision)
Statements.createTextRevision
    (TextRevisionID -> Transaction (Vector CommentAnchor))
-> Transaction TextRevision
textRevision ((TextRevisionID -> Transaction (Vector CommentAnchor))
 -> Transaction TextRevision)
-> (TextRevisionID -> Transaction (Vector CommentAnchor))
-> Transaction TextRevision
forall a b. (a -> b) -> a -> b
$
        \TextRevisionID
rev ->
            ((TextRevisionID, CommentAnchor) -> Transaction CommentAnchor)
-> Vector (TextRevisionID, CommentAnchor)
-> Transaction (Vector CommentAnchor)
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 ((TextRevisionID, CommentAnchor)
-> Statement (TextRevisionID, CommentAnchor) CommentAnchor
-> Transaction CommentAnchor
forall a b. a -> Statement a b -> Transaction b
`statement` Statement (TextRevisionID, CommentAnchor) CommentAnchor
Statements.putCommentAnchor) ((TextRevisionID
rev,) (CommentAnchor -> (TextRevisionID, CommentAnchor))
-> Vector CommentAnchor -> Vector (TextRevisionID, CommentAnchor)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Vector CommentAnchor
commentAnchors)

isTextElementInDocument :: DocumentID -> Transaction (TextElementID -> Bool)
isTextElementInDocument :: DocumentID -> Transaction (TextElementID -> Bool)
isTextElementInDocument DocumentID
docID =
    DocumentID
-> Statement DocumentID (Vector TextElementID)
-> Transaction (Vector TextElementID)
forall a b. a -> Statement a b -> Transaction b
statement DocumentID
docID Statement DocumentID (Vector TextElementID)
Statements.getTextElementIDsForDocument
        Transaction (Vector TextElementID)
-> (Vector TextElementID -> TextElementID -> Bool)
-> Transaction (TextElementID -> Bool)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> (TextElementID -> Set TextElementID -> Bool)
-> Set TextElementID -> TextElementID -> Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip TextElementID -> Set TextElementID -> Bool
forall a. Ord a => a -> Set a -> Bool
Set.member (Set TextElementID -> TextElementID -> Bool)
-> (Vector TextElementID -> Set TextElementID)
-> Vector TextElementID
-> TextElementID
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TextElementID] -> Set TextElementID
forall a. Ord a => [a] -> Set a
Set.fromList ([TextElementID] -> Set TextElementID)
-> (Vector TextElementID -> [TextElementID])
-> Vector TextElementID
-> Set TextElementID
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector TextElementID -> [TextElementID]
forall a. Vector a -> [a]
Vector.toList

createTreeRevision
    :: UserID
    -> DocumentID
    -> Node TextElementID
    -> Transaction (TreeRevision TextElementID)
createTreeRevision :: UserID
-> DocumentID
-> Node TextElementID
-> Transaction (TreeRevision TextElementID)
createTreeRevision UserID
authorID DocumentID
docID Node TextElementID
rootNode = do
    Hash
rootHash <- Node TextElementID -> Transaction Hash
putTree Node TextElementID
rootNode
    Maybe (Hash, Node TextElementID -> TreeRevision TextElementID)
current <-
        TreeRevisionRef
-> Statement
     TreeRevisionRef
     (Maybe (Hash, Node TextElementID -> TreeRevision TextElementID))
-> Transaction
     (Maybe (Hash, Node TextElementID -> TreeRevision TextElementID))
forall a b. a -> Statement a b -> Transaction b
statement
            (DocumentID -> TreeRevisionSelector -> TreeRevisionRef
TreeRevisionRef DocumentID
docID TreeRevisionSelector
TreeRevision.Latest)
            Statement
  TreeRevisionRef
  (Maybe (Hash, Node TextElementID -> TreeRevision TextElementID))
forall a.
Statement TreeRevisionRef (Maybe (Hash, Node a -> TreeRevision a))
Statements.getTreeRevision
    let keepCurrent :: Maybe (Node TextElementID -> TreeRevision TextElementID)
keepCurrent = Maybe (Hash, Node TextElementID -> TreeRevision TextElementID)
current Maybe (Hash, Node TextElementID -> TreeRevision TextElementID)
-> ((Hash, Node TextElementID -> TreeRevision TextElementID)
    -> Maybe ())
-> Maybe ()
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ())
-> ((Hash, Node TextElementID -> TreeRevision TextElementID)
    -> Bool)
-> (Hash, Node TextElementID -> TreeRevision TextElementID)
-> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Hash -> Hash -> Bool
forall a. Eq a => a -> a -> Bool
== Hash
rootHash) (Hash -> Bool)
-> ((Hash, Node TextElementID -> TreeRevision TextElementID)
    -> Hash)
-> (Hash, Node TextElementID -> TreeRevision TextElementID)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Hash, Node TextElementID -> TreeRevision TextElementID) -> Hash
forall a b. (a, b) -> a
fst) Maybe ()
-> Maybe (Hash, Node TextElementID -> TreeRevision TextElementID)
-> Maybe (Hash, Node TextElementID -> TreeRevision TextElementID)
forall a b. Maybe a -> Maybe b -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Maybe (Hash, Node TextElementID -> TreeRevision TextElementID)
current Maybe (Hash, Node TextElementID -> TreeRevision TextElementID)
-> ((Hash, Node TextElementID -> TreeRevision TextElementID)
    -> Node TextElementID -> TreeRevision TextElementID)
-> Maybe (Node TextElementID -> TreeRevision TextElementID)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> (Hash, Node TextElementID -> TreeRevision TextElementID)
-> Node TextElementID -> TreeRevision TextElementID
forall a b. (a, b) -> b
snd
    case Maybe (Node TextElementID -> TreeRevision TextElementID)
keepCurrent of
        Just Node TextElementID -> TreeRevision TextElementID
currentRevision ->
            TreeRevision TextElementID
-> Transaction (TreeRevision TextElementID)
forall a. a -> Transaction a
forall (m :: * -> *) a. Monad m => a -> m a
return (TreeRevision TextElementID
 -> Transaction (TreeRevision TextElementID))
-> TreeRevision TextElementID
-> Transaction (TreeRevision TextElementID)
forall a b. (a -> b) -> a -> b
$ Node TextElementID -> TreeRevision TextElementID
currentRevision Node TextElementID
rootNode
        Maybe (Node TextElementID -> TreeRevision TextElementID)
Nothing -> do
            Node TextElementID -> TreeRevision TextElementID
revision <-
                (DocumentID, UserID, Hash)
-> Statement
     (DocumentID, UserID, Hash)
     (Node TextElementID -> TreeRevision TextElementID)
-> Transaction (Node TextElementID -> TreeRevision TextElementID)
forall a b. a -> Statement a b -> Transaction b
statement
                    (DocumentID
docID, UserID
authorID, Hash
rootHash)
                    Statement
  (DocumentID, UserID, Hash)
  (Node TextElementID -> TreeRevision TextElementID)
forall a.
Statement (DocumentID, UserID, Hash) (Node a -> TreeRevision a)
Statements.putTreeRevision
            TreeRevision TextElementID
-> Transaction (TreeRevision TextElementID)
forall a. a -> Transaction a
forall (m :: * -> *) a. Monad m => a -> m a
return (TreeRevision TextElementID
 -> Transaction (TreeRevision TextElementID))
-> TreeRevision TextElementID
-> Transaction (TreeRevision TextElementID)
forall a b. (a -> b) -> a -> b
$ Node TextElementID -> TreeRevision TextElementID
revision Node TextElementID
rootNode

putTree :: Node TextElementID -> Transaction Hash
putTree :: Node TextElementID -> Transaction Hash
putTree (Node NodeHeader
metaData [Tree TextElementID]
children) = do
    [TreeEdgeChildRef]
childRefs <- (Tree TextElementID -> Transaction TreeEdgeChildRef)
-> [Tree TextElementID] -> Transaction [TreeEdgeChildRef]
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) -> [a] -> m [b]
mapM Tree TextElementID -> Transaction TreeEdgeChildRef
putSubTree [Tree TextElementID]
children
    let ownHash :: Hash
ownHash =
            ByteString -> Hash
Hash (ByteString -> Hash) -> ByteString -> Hash
forall a b. (a -> b) -> a -> b
$
                Ctx -> ByteString
SHA1.finalize (Ctx -> ByteString) -> Ctx -> ByteString
forall a b. (a -> b) -> a -> b
$
                    ((TreeEdgeChildRef, Int) -> Ctx -> Ctx)
-> Ctx -> [(TreeEdgeChildRef, Int)] -> Ctx
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr
                        (\(TreeEdgeChildRef
child, Int
idx) -> (Ctx -> Int -> Ctx
forall a. Hashable a => Ctx -> a -> Ctx
`updateHash` Int
idx) (Ctx -> Ctx) -> (Ctx -> Ctx) -> Ctx -> Ctx
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Ctx -> TreeEdgeChildRef -> Ctx) -> TreeEdgeChildRef -> Ctx -> Ctx
forall a b c. (a -> b -> c) -> b -> a -> c
flip Ctx -> TreeEdgeChildRef -> Ctx
forall a. Hashable a => Ctx -> a -> Ctx
updateHash TreeEdgeChildRef
child)
                        (Ctx -> NodeHeader -> Ctx
forall a. Hashable a => Ctx -> a -> Ctx
updateHash Ctx
SHA1.init NodeHeader
metaData)
                        ([TreeEdgeChildRef] -> [Int] -> [(TreeEdgeChildRef, Int)]
forall a b. [a] -> [b] -> [(a, b)]
zip [TreeEdgeChildRef]
childRefs [Int
0 :: Int ..])
    (Hash, NodeHeader)
-> Statement (Hash, NodeHeader) () -> Transaction ()
forall a b. a -> Statement a b -> Transaction b
statement (Hash
ownHash, NodeHeader
metaData) Statement (Hash, NodeHeader) ()
Statements.putTreeNode
    let toEdge :: TreeEdgeChildRef -> GroupID -> TreeEdge
toEdge TreeEdgeChildRef
ref GroupID
idx =
            TreeEdge
                { parentHash :: Hash
TreeEdge.parentHash = Hash
ownHash
                , position :: GroupID
TreeEdge.position = GroupID
idx
                , child :: TreeEdgeChildRef
TreeEdge.child = TreeEdgeChildRef
ref
                }
    let edges :: [TreeEdge]
edges = (TreeEdgeChildRef -> GroupID -> TreeEdge)
-> [TreeEdgeChildRef] -> [GroupID] -> [TreeEdge]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith TreeEdgeChildRef -> GroupID -> TreeEdge
toEdge [TreeEdgeChildRef]
childRefs [GroupID
0 ..]
    (TreeEdge -> Transaction ()) -> [TreeEdge] -> Transaction ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (TreeEdge -> Statement TreeEdge () -> Transaction ()
forall a b. a -> Statement a b -> Transaction b
`statement` Statement TreeEdge ()
Statements.putTreeEdge) [TreeEdge]
edges
    Hash -> Transaction Hash
forall a. a -> Transaction a
forall (m :: * -> *) a. Monad m => a -> m a
return Hash
ownHash
  where
    putSubTree :: Tree TextElementID -> Transaction TreeEdgeChildRef
    putSubTree :: Tree TextElementID -> Transaction TreeEdgeChildRef
putSubTree (Leaf TextElementID
id_) = TreeEdgeChildRef -> Transaction TreeEdgeChildRef
forall a. a -> Transaction a
forall (m :: * -> *) a. Monad m => a -> m a
return (TreeEdgeChildRef -> Transaction TreeEdgeChildRef)
-> TreeEdgeChildRef -> Transaction TreeEdgeChildRef
forall a b. (a -> b) -> a -> b
$ TextElementID -> TreeEdgeChildRef
TreeEdgeRefToTextElement TextElementID
id_
    putSubTree (Tree Node TextElementID
node) = Node TextElementID -> Transaction Hash
putTree Node TextElementID
node Transaction Hash
-> (Hash -> TreeEdgeChildRef) -> Transaction TreeEdgeChildRef
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> Hash -> TreeEdgeChildRef
TreeEdgeRefToNode

hasPermission :: UserID -> DocumentID -> Permission -> Transaction Bool
hasPermission :: UserID -> DocumentID -> Permission -> Transaction Bool
hasPermission UserID
userID DocumentID
docID Permission
perms =
    (UserID, DocumentID, Permission)
-> Statement (UserID, DocumentID, Permission) Bool
-> Transaction Bool
forall a b. a -> Statement a b -> Transaction b
statement (UserID
userID, DocumentID
docID, Permission
perms) Statement (UserID, DocumentID, Permission) Bool
Statements.hasPermission

isGroupAdmin :: UserID -> GroupID -> Transaction Bool
isGroupAdmin :: UserID -> GroupID -> Transaction Bool
isGroupAdmin UserID
userID GroupID
groupID =
    (UserID, GroupID)
-> Statement (UserID, GroupID) Bool -> Transaction Bool
forall a b. a -> Statement a b -> Transaction b
statement (UserID
userID, GroupID
groupID) Statement (UserID, GroupID) Bool
Statements.isGroupAdmin

createComment :: UserID -> TextElementID -> Text -> Transaction Comment
createComment :: UserID -> TextElementID -> Text -> Transaction Comment
createComment UserID
userID TextElementID
textElemID Text
text =
    (UserID, TextElementID, Text)
-> Statement (UserID, TextElementID, Text) Comment
-> Transaction Comment
forall a b. a -> Statement a b -> Transaction b
statement (UserID
userID, TextElementID
textElemID, Text
text) Statement (UserID, TextElementID, Text) Comment
Statements.createComment

existsComment :: CommentRef -> Transaction Bool
existsComment :: CommentRef -> Transaction Bool
existsComment =
    (CommentRef -> Statement CommentRef Bool -> Transaction Bool)
-> Statement CommentRef Bool -> CommentRef -> Transaction Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip CommentRef -> Statement CommentRef Bool -> Transaction Bool
forall a b. a -> Statement a b -> Transaction b
statement Statement CommentRef Bool
Statements.existsComment

resolveComment :: CommentID -> Transaction ()
resolveComment :: CommentID -> Transaction ()
resolveComment =
    (CommentID -> Statement CommentID () -> Transaction ())
-> Statement CommentID () -> CommentID -> Transaction ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip CommentID -> Statement CommentID () -> Transaction ()
forall a b. a -> Statement a b -> Transaction b
statement Statement CommentID ()
Statements.resolveComment

createReply :: UserID -> CommentID -> Text -> Transaction Message
createReply :: UserID -> CommentID -> Text -> Transaction Message
createReply UserID
userID CommentID
commentID = ((UserID, CommentID, Text)
-> Statement (UserID, CommentID, Text) Message
-> Transaction Message
forall a b. a -> Statement a b -> Transaction b
`statement` Statement (UserID, CommentID, Text) Message
Statements.createReply) ((UserID, CommentID, Text) -> Transaction Message)
-> (Text -> (UserID, CommentID, Text))
-> Text
-> Transaction Message
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UserID
userID,CommentID
commentID,)

logMessage
    :: (ToJSON v)
    => Severity
    -- ^ severity of the log message
    -> Maybe UserID
    -- ^ source user
    -> Scope
    -- ^ scope (e.g, "docs.text.revision")
    -> v
    -- ^ content (json)
    -> Transaction LogMessage
    -- ^ created log message
logMessage :: forall v.
ToJSON v =>
Severity -> Maybe UserID -> Scope -> v -> Transaction LogMessage
logMessage Severity
severity Maybe UserID
source Scope
scope v
content =
    (Severity, Maybe UserID, Scope, v)
-> Statement (Severity, Maybe UserID, Scope, v) LogMessage
-> Transaction LogMessage
forall a b. a -> Statement a b -> Transaction b
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

updateLatestTitle :: TextElementID -> Text -> Transaction ()
updateLatestTitle :: TextElementID -> Text -> Transaction ()
updateLatestTitle = ((TextElementID, Text) -> Transaction ())
-> TextElementID -> Text -> Transaction ()
forall a b c. ((a, b) -> c) -> a -> b -> c
curry ((TextElementID, Text)
-> Statement (TextElementID, Text) () -> Transaction ()
forall a b. a -> Statement a b -> Transaction b
`statement` Statement (TextElementID, Text) ()
Statements.updateLatestTitle)

getTextElement :: TextElementID -> Transaction (Maybe TextElement)
getTextElement :: TextElementID -> Transaction (Maybe TextElement)
getTextElement = (TextElementID
 -> Statement TextElementID (Maybe TextElement)
 -> Transaction (Maybe TextElement))
-> Statement TextElementID (Maybe TextElement)
-> TextElementID
-> Transaction (Maybe TextElement)
forall a b c. (a -> b -> c) -> b -> a -> c
flip TextElementID
-> Statement TextElementID (Maybe TextElement)
-> Transaction (Maybe TextElement)
forall a b. a -> Statement a b -> Transaction b
statement Statement TextElementID (Maybe TextElement)
Statements.getTextElement

-- | Create or update a draft text revision for a user
createDraftTextRevision
    :: UserID
    -> TextElementRef
    -> TextRevisionID
    -> Text
    -> Vector CommentAnchor
    -> Transaction DraftRevision
createDraftTextRevision :: UserID
-> TextElementRef
-> TextRevisionID
-> Text
-> Vector CommentAnchor
-> Transaction DraftRevision
createDraftTextRevision UserID
userID (TextElementRef DocumentID
_ TextElementID
textID) TextRevisionID
basedOnRevision Text
content Vector CommentAnchor
commentAnchors = do
    (DraftRevisionID -> Transaction (Vector CommentAnchor))
-> Transaction DraftRevision
draftRevision <-
        (TextElementID, TextRevisionID, UserID, Text)
-> Statement
     (TextElementID, TextRevisionID, UserID, Text)
     ((DraftRevisionID -> Transaction (Vector CommentAnchor))
      -> Transaction DraftRevision)
-> Transaction
     ((DraftRevisionID -> Transaction (Vector CommentAnchor))
      -> Transaction DraftRevision)
forall a b. a -> Statement a b -> Transaction b
statement
            (TextElementID
textID, TextRevisionID
basedOnRevision, UserID
userID, Text
content)
            Statement
  (TextElementID, TextRevisionID, UserID, Text)
  ((DraftRevisionID -> Transaction (Vector CommentAnchor))
   -> Transaction DraftRevision)
forall (m :: * -> *).
Monad m =>
Statement
  (TextElementID, TextRevisionID, UserID, Text)
  ((DraftRevisionID -> m (Vector CommentAnchor)) -> m DraftRevision)
Statements.createDraftTextRevision
    (DraftRevisionID -> Transaction (Vector CommentAnchor))
-> Transaction DraftRevision
draftRevision ((DraftRevisionID -> Transaction (Vector CommentAnchor))
 -> Transaction DraftRevision)
-> (DraftRevisionID -> Transaction (Vector CommentAnchor))
-> Transaction DraftRevision
forall a b. (a -> b) -> a -> b
$ \DraftRevisionID
draftId ->
        ((DraftRevisionID, Vector CommentAnchor) -> Transaction ())
-> [(DraftRevisionID, Vector CommentAnchor)] -> Transaction ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_
            ((DraftRevisionID, Vector CommentAnchor)
-> Statement (DraftRevisionID, Vector CommentAnchor) ()
-> Transaction ()
forall a b. a -> Statement a b -> Transaction b
`statement` Statement (DraftRevisionID, Vector CommentAnchor) ()
Statements.putDraftCommentAnchors)
            [(DraftRevisionID
draftId, Vector CommentAnchor
commentAnchors)]
            Transaction ()
-> Transaction (Vector CommentAnchor)
-> Transaction (Vector CommentAnchor)
forall a b. Transaction a -> Transaction b -> Transaction b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> DraftRevisionID
-> Statement DraftRevisionID (Vector CommentAnchor)
-> Transaction (Vector CommentAnchor)
forall a b. a -> Statement a b -> Transaction b
statement DraftRevisionID
draftId Statement DraftRevisionID (Vector CommentAnchor)
Statements.getDraftCommentAnchors

-- | Get draft revision for a text element by a specific user
getDraftTextRevision
    :: UserID -> TextElementRef -> Transaction (Maybe DraftRevision)
getDraftTextRevision :: UserID -> TextElementRef -> Transaction (Maybe DraftRevision)
getDraftTextRevision UserID
userID (TextElementRef DocumentID
_ TextElementID
textID) = do
    (DraftRevisionID -> Transaction (Vector CommentAnchor))
-> Transaction (Maybe DraftRevision)
draftGetter <- (TextElementID, UserID)
-> Statement
     (TextElementID, UserID)
     ((DraftRevisionID -> Transaction (Vector CommentAnchor))
      -> Transaction (Maybe DraftRevision))
-> Transaction
     ((DraftRevisionID -> Transaction (Vector CommentAnchor))
      -> Transaction (Maybe DraftRevision))
forall a b. a -> Statement a b -> Transaction b
statement (TextElementID
textID, UserID
userID) Statement
  (TextElementID, UserID)
  ((DraftRevisionID -> Transaction (Vector CommentAnchor))
   -> Transaction (Maybe DraftRevision))
forall (m :: * -> *).
Monad m =>
Statement
  (TextElementID, UserID)
  ((DraftRevisionID -> m (Vector CommentAnchor))
   -> m (Maybe DraftRevision))
Statements.getDraftTextRevision
    (DraftRevisionID -> Transaction (Vector CommentAnchor))
-> Transaction (Maybe DraftRevision)
draftGetter (DraftRevisionID
-> Statement DraftRevisionID (Vector CommentAnchor)
-> Transaction (Vector CommentAnchor)
forall a b. a -> Statement a b -> Transaction b
`statement` Statement DraftRevisionID (Vector CommentAnchor)
Statements.getDraftCommentAnchors)

-- | Delete draft revision for a text element by a user
deleteDraftTextRevision :: UserID -> TextElementRef -> Transaction ()
deleteDraftTextRevision :: UserID -> TextElementRef -> Transaction ()
deleteDraftTextRevision UserID
userID (TextElementRef DocumentID
_ TextElementID
textID) =
    (TextElementID, UserID)
-> Statement (TextElementID, UserID) () -> Transaction ()
forall a b. a -> Statement a b -> Transaction b
statement (TextElementID
textID, UserID
userID) Statement (TextElementID, UserID) ()
Statements.deleteDraftTextRevision

getTreeRevision
    :: TreeRevisionRef
    -> Transaction (Maybe (TreeRevision TextElement))
getTreeRevision :: TreeRevisionRef -> Transaction (Maybe (TreeRevision TextElement))
getTreeRevision TreeRevisionRef
ref = do
    Maybe (Hash, Node TextElement -> TreeRevision TextElement)
revision <- Transaction
  (Maybe (Hash, Node TextElement -> TreeRevision TextElement))
forall {a}. Transaction (Maybe (Hash, Node a -> TreeRevision a))
getRevision
    case Maybe (Hash, Node TextElement -> TreeRevision TextElement)
revision of
        Just (Hash
rootHash, Node TextElement -> TreeRevision TextElement
treeRevision) -> do
            Node TextElement
root <- Hash -> Transaction (Node TextElement)
getTree Hash
rootHash
            Maybe (TreeRevision TextElement)
-> Transaction (Maybe (TreeRevision TextElement))
forall a. a -> Transaction a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (TreeRevision TextElement)
 -> Transaction (Maybe (TreeRevision TextElement)))
-> Maybe (TreeRevision TextElement)
-> Transaction (Maybe (TreeRevision TextElement))
forall a b. (a -> b) -> a -> b
$ TreeRevision TextElement -> Maybe (TreeRevision TextElement)
forall a. a -> Maybe a
Just (TreeRevision TextElement -> Maybe (TreeRevision TextElement))
-> TreeRevision TextElement -> Maybe (TreeRevision TextElement)
forall a b. (a -> b) -> a -> b
$ Node TextElement -> TreeRevision TextElement
treeRevision Node TextElement
root
        Maybe (Hash, Node TextElement -> TreeRevision TextElement)
Nothing -> Maybe (TreeRevision TextElement)
-> Transaction (Maybe (TreeRevision TextElement))
forall a. a -> Transaction a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (TreeRevision TextElement)
forall a. Maybe a
Nothing
  where
    getRevision :: Transaction (Maybe (Hash, Node a -> TreeRevision a))
getRevision = TreeRevisionRef
-> Statement
     TreeRevisionRef (Maybe (Hash, Node a -> TreeRevision a))
-> Transaction (Maybe (Hash, Node a -> TreeRevision a))
forall a b. a -> Statement a b -> Transaction b
statement TreeRevisionRef
ref Statement TreeRevisionRef (Maybe (Hash, Node a -> TreeRevision a))
forall a.
Statement TreeRevisionRef (Maybe (Hash, Node a -> TreeRevision a))
Statements.getTreeRevision

getTree :: Hash -> Transaction (Node TextElement)
getTree :: Hash -> Transaction (Node TextElement)
getTree Hash
rootHash = do
    NodeHeader
rootHeader <- Hash -> Statement Hash NodeHeader -> Transaction NodeHeader
forall a b. a -> Statement a b -> Transaction b
statement Hash
rootHash Statement Hash NodeHeader
Statements.getTreeNode
    Hash -> NodeHeader -> Transaction (Node TextElement)
fromHeader Hash
rootHash NodeHeader
rootHeader
  where
    fromHeader :: Hash -> NodeHeader -> Transaction (Node TextElement)
    fromHeader :: Hash -> NodeHeader -> Transaction (Node TextElement)
fromHeader Hash
hash' NodeHeader
header = do
        Vector TreeEdgeChild
children <- Hash
-> Statement Hash (Vector TreeEdgeChild)
-> Transaction (Vector TreeEdgeChild)
forall a b. a -> Statement a b -> Transaction b
statement Hash
hash' Statement Hash (Vector TreeEdgeChild)
Statements.getTreeEdgesByParent
        Vector (Tree TextElement)
edges <- (TreeEdgeChild -> Transaction (Tree TextElement))
-> Vector TreeEdgeChild -> Transaction (Vector (Tree TextElement))
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 TreeEdgeChild -> Transaction (Tree TextElement)
edgeSelector Vector TreeEdgeChild
children
        Node TextElement -> Transaction (Node TextElement)
forall a. a -> Transaction a
forall (m :: * -> *) a. Monad m => a -> m a
return (Node TextElement -> Transaction (Node TextElement))
-> Node TextElement -> Transaction (Node TextElement)
forall a b. (a -> b) -> a -> b
$ NodeHeader -> [Tree TextElement] -> Node TextElement
forall a. NodeHeader -> [Tree a] -> Node a
Node NodeHeader
header ([Tree TextElement] -> Node TextElement)
-> [Tree TextElement] -> Node TextElement
forall a b. (a -> b) -> a -> b
$ Vector (Tree TextElement) -> [Tree TextElement]
forall a. Vector a -> [a]
Vector.toList Vector (Tree TextElement)
edges
    edgeSelector :: TreeEdgeChild -> Transaction (Tree TextElement)
    edgeSelector :: TreeEdgeChild -> Transaction (Tree TextElement)
edgeSelector TreeEdgeChild
edge =
        case TreeEdgeChild
edge of
            (TreeEdgeToTextElement TextElement
textElement) -> Tree TextElement -> Transaction (Tree TextElement)
forall a. a -> Transaction a
forall (m :: * -> *) a. Monad m => a -> m a
return (Tree TextElement -> Transaction (Tree TextElement))
-> Tree TextElement -> Transaction (Tree TextElement)
forall a b. (a -> b) -> a -> b
$ TextElement -> Tree TextElement
forall a. a -> Tree a
Tree.Leaf TextElement
textElement
            (TreeEdgeToNode Hash
hash' NodeHeader
header) -> Hash -> NodeHeader -> Transaction (Node TextElement)
fromHeader Hash
hash' NodeHeader
header Transaction (Node TextElement)
-> (Node TextElement -> Tree TextElement)
-> Transaction (Tree TextElement)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> Node TextElement -> Tree TextElement
forall a. Node a -> Tree a
Tree.Tree

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

getRevisionKey :: RevisionRef -> Transaction (Maybe RevisionKey)
getRevisionKey :: RevisionRef -> Transaction (Maybe RevisionKey)
getRevisionKey (RevisionRef DocumentID
docID RevisionSelector
revID) =
    (DocumentID, RevisionSelector)
-> Statement (DocumentID, RevisionSelector) (Maybe RevisionKey)
-> Transaction (Maybe RevisionKey)
forall a b. a -> Statement a b -> Transaction b
statement (DocumentID
docID, RevisionSelector
revID) Statement (DocumentID, RevisionSelector) (Maybe RevisionKey)
Statements.getRevisionKey

getDocument :: DocumentID -> Transaction (Maybe Document)
getDocument :: DocumentID -> Transaction (Maybe Document)
getDocument = (DocumentID
-> Statement DocumentID (Maybe Document)
-> Transaction (Maybe Document)
forall a b. a -> Statement a b -> Transaction b
`statement` Statement DocumentID (Maybe Document)
Statements.getDocument)

getDocuments :: UserID -> Transaction (Vector Document)
getDocuments :: UserID -> Transaction (Vector Document)
getDocuments = (UserID
-> Statement UserID (Vector Document)
-> Transaction (Vector Document)
forall a b. a -> Statement a b -> Transaction b
`statement` Statement UserID (Vector Document)
Statements.getDocuments)

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