-
Notifications
You must be signed in to change notification settings - Fork 0
/
DB.hs
146 lines (121 loc) · 4.94 KB
/
DB.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Use camelCase" #-}
module DB
(
getUsers,
getPhrases,
getPhrasesFromUser,
getUnapprovedPhrases,
getUserPassword,
postPhrase,
updateApproved,
getRootAuthor
) where
import Data.Int
import Hasql.Statement
import Hasql.Session
import Servant
import Data.ByteString.UTF8 (fromString)
import Data.Profunctor
import Control.Monad.Error.Class (MonadError)
import Control.Monad (void)
import qualified Hasql.Session as Session
import qualified Hasql.TH as TH
import qualified Data.Text as T
import qualified Data.Vector as V
import qualified Data.ByteString.Lazy as LBS
import Types
import Data.Maybe
import qualified API
getUsers :: (MonadDB m, MonadError ServerError m) => m [User]
getUsers = _run_simple () allUsers
getPhrases :: (MonadDB m, MonadError ServerError m) => m [Phrase]
getPhrases = _run_simple () allPhrases
getUserPassword :: (MonadDB m, MonadError ServerError m) => T.Text -> m (Int32, T.Text)
getUserPassword t = do
creds <- _run_simple t userPassword
case creds of
Just result -> return result
Nothing -> throwError err500 {errBody = LBS.fromStrict "Couldn't find this user"}
getPhrasesFromUser :: (MonadDB m, MonadError ServerError m) => Int32 -> m [Phrase]
getPhrasesFromUser i = _run_simple i phrasesFromUser
getUnapprovedPhrases :: (MonadDB m, MonadError ServerError m) => m [Phrase]
getUnapprovedPhrases = _run_simple () unapprovedPhrases
postPhrase :: (MonadDB m, MonadError ServerError m) => User -> API.PostPhrase -> m ()
postPhrase user t = Control.Monad.void (_run_simple (API.text t, user_id user, API.reply_to t) insertPhrase)
updateApproved :: (MonadDB m, MonadError ServerError m) => Int32 -> Maybe Bool -> m ()
updateApproved i approved = Control.Monad.void (_run_simple (fromMaybe True approved, i) markApproved)
getRootAuthor :: (MonadDB m, MonadError ServerError m) => Int32 -> m Int32
getRootAuthor p_id = do
result <- _run p_id session
case result of
Just u_id -> return u_id
Nothing -> throwError err500 {errBody = LBS.fromStrict "Couldn't find the author"}
where session :: Int32 -> Session (Maybe Int32)
session x = do
root_author <- Session.statement x rootAuthor
case root_author of
Just result -> return (Just result)
Nothing -> Session.statement x phraseAuthorById
simpleSession :: Statement params result -> params -> Session result
simpleSession a = (`Session.statement` a)
_run_simple :: (MonadDB m, MonadError ServerError m) => params -> Statement params result -> m result
_run_simple arg sess = _run arg (simpleSession sess)
_run :: (MonadDB m, MonadError ServerError m) => params -> (params -> Session result) -> m result
_run arg sess = do
result <- runSession (sess arg)
case result of
Right usage_result -> pure usage_result
Left usage_error -> parseUsageError usage_error
where
parseUsageError r = throw500 (fromString $ show r)
throw500 msg = throwError err500 {errBody = LBS.fromStrict msg}
getListWith :: Statement a (V.Vector c) -> (c -> b) -> Statement a [b]
getListWith s fromTuple = rmap fromTuples s
where
fromTuples vec = V.toList $ V.map fromTuple vec
allUsers :: Statement () [User]
allUsers = [TH.vectorStatement|
select id:: int4, name::text
from "author"|] `getListWith` tupleToUser
userPassword :: Statement T.Text (Maybe (Int32, T.Text))
userPassword = [TH.maybeStatement|
select id::int4, password::text
from "author"
where name = $1::text|]
allPhrases :: Statement () [Phrase]
allPhrases = [TH.vectorStatement|
select id:: int4, text::text, author_id::int4, approved::bool
from "phrase"|] `getListWith` tupleToPhrase
phrasesFromUser :: Statement Int32 [Phrase]
phrasesFromUser = [TH.vectorStatement|
select id::int4, text::text, author_id::int4, approved::bool
from "phrase"
where author_id = $1 ::Int4|] `getListWith` tupleToPhrase
unapprovedPhrases :: Statement () [Phrase]
unapprovedPhrases = [TH.vectorStatement|
select id::int4, text::text, author_id::int4, approved::bool
from "phrase"
where approved = false|] `getListWith` tupleToPhrase
insertPhrase:: Statement (T.Text, Int32, Maybe Int32) Int32
insertPhrase= [TH.singletonStatement|
insert into "phrase" (text, author_id, parent_id)
values ($1::text, $2::int4, $3::int4?)
returning id::int4|]
markApproved :: Statement (Bool, Int32) Int32
markApproved = [TH.singletonStatement|
update "phrase"
set approved = $1::bool
where id = $2::int4
returning 1::int4|]
rootAuthor :: Statement Int32 (Maybe Int32)
rootAuthor = [TH.maybeStatement|
select A.author_id::int4
from phrase A, phrase B
where A.id = B.parent_id AND B.id = $1::int4
|]
phraseAuthorById :: Statement Int32 (Maybe Int32)
phraseAuthorById = [TH.maybeStatement|
select author_id::int4
from phrase
where id = $1::int4|]