@@ -20,7 +20,12 @@ module Data.Query (
20 getPostsForUser,
20 getPostsForUser,
22 publishPost,
22 publishPost,
23− PostResponse(..)
23+ PostResponse(..),
28+ getAnonymousChannelPosts
26import Squeal.PostgreSQL
31import Squeal.PostgreSQL
@@ -37,12 +42,20 @@ import Data.Word (Word64)
37import qualified Data.List as L
42import qualified Data.List as L
38import qualified Data.Map.Strict as M
43import qualified Data.Map.Strict as M
39import qualified Data.Set as Set
44import qualified Data.Set as Set
45+import qualified Generics.SOP as SOP
46+import qualified GHC.Generics as GHC
40import Data.Int (Int64)
47import Data.Int (Int64)
41import Data.ByteString.Char8 (pack)
48import Data.ByteString.Char8 (pack)
42import GHC.Generics (Generic)
49import GHC.Generics (Generic)
43import Data.Aeson
50import Data.Aeson
44import Servant.Docs
51import Servant.Docs
45import Data.Proxy
52import Data.Proxy
53+import Data.Vector (Vector, toList)
54+import Data.Text (Text)
55+import Data.Channel.NamedChannel
56+import Data.Channel.AnonymousChannel
57+import Data.Maybe (fromMaybe)
58+import Data.Monoid (mempty)
47-- MARK: Documentation
60-- MARK: Documentation
@@ -68,6 +81,7 @@ getPostQ oTe te = select (
68 #posts ! #postRowId :*
81 #posts ! #postRowId :*
69 #posts ! #postRowAuthorId :*
82 #posts ! #postRowAuthorId :*
70 #posts ! #postRowUpdateTime :*
83 #posts ! #postRowUpdateTime :*
84+ #posts ! #postRowTags :*
71 -- #rowElementId :*
85 -- #rowElementId :*
72 #postElements ! #rowElementOrd :*
86 #postElements ! #rowElementOrd :*
73 #postElements ! #rowElementMarkdown :*
87 #postElements ! #rowElementMarkdown :*
@@ -128,11 +142,12 @@ getLastNPostsForUserQ lim = getPostQ
128 (limit lim . orderBy [#posts ! #postRowUpdateTime & Desc])
142 (limit lim . orderBy [#posts ! #postRowUpdateTime & Desc])
129 (where_ $ #postRowAuthorId .== param @1)
143 (where_ $ #postRowAuthorId .== param @1)
131−createPostRowQ :: Manipulation Schema (TuplePG (Only UserId)) (RowPG (Only PostId))
145+createPostRowQ :: Manipulation Schema (TuplePG (UserId, Vector Text)) (RowPG (Only PostId))
132createPostRowQ = insertRow #posts
146createPostRowQ = insertRow #posts
133 (Default `as` #postRowId :*
147 (Default `as` #postRowId :*
134 Set (param @1) `as` #postRowAuthorId :*
148 Set (param @1) `as` #postRowAuthorId :*
135− Set currentTimestamp `as` #postRowUpdateTime)
149+ Set currentTimestamp `as` #postRowUpdateTime :*
150+ Set (param @2) `as` #postRowTags)
136 OnConflictDoRaise
151 OnConflictDoRaise
137 (Returning $ #postRowId `as` #fromOnly)
152 (Returning $ #postRowId `as` #fromOnly)
@@ -146,6 +161,52 @@ createPostElementRowsQ = insertRow_ #postElements
146 Set (param @6) `as` #rowElementQuote :*
161 Set (param @6) `as` #rowElementQuote :*
147 Set (param @7) `as` #rowElementAttachment)
162 Set (param @7) `as` #rowElementAttachment)
164+getChannelForUserQ :: Query Schema (TuplePG (UserId, NamedChannelId)) (RowPG NamedChannelFull)
165+getChannelForUserQ = selectStar $
166+ from (table #channels) &
167+ where_ (#namedChannelFullOwner .== param @1 .&& #namedChannelFullId .== param @2)
169+getAllChannelsForUserQ :: Query Schema (TuplePG (Only UserId)) (RowPG NamedChannelFull)
170+getAllChannelsForUserQ = selectStar $
171+ from (table #channels) &
172+ where_ (#namedChannelFullOwner .== param @1)
174+createNamedChannelQ :: Manipulation Schema (TuplePG NamedChannelCreation) (RowPG (Only NamedChannelId))
175+createNamedChannelQ = insertRow #channels
177+ Default `as` #namedChannelFullId :*
178+ Set (param @1) `as` #namedChannelFullOwner :*
179+ Set (param @2) `as` #namedChannelFullName :*
180+ Set (param @3) `as` #namedChannelFullTags :*
181+ Set (param @4) `as` #namedChannelFullPeopleIds
184+ (Returning $ #namedChannelFullId `as` #fromOnly)
186+updateNamedChannelQ :: Manipulation Schema (TuplePG NamedChannelFull) '[]
187+updateNamedChannelQ = update_ #channels
189+ Same `as` #namedChannelFullId :*
190+ Same `as` #namedChannelFullOwner :*
191+ Set (param @3) `as` #namedChannelFullName :*
192+ Set (param @4) `as` #namedChannelFullTags :*
193+ Set (param @5) `as` #namedChannelFullPeopleIds
195+ (#namedChannelFullOwner .== param @2 .&& #namedChannelFullId .== param @2)
198+getChannelPostsQ :: Limit -> [UserId] -> Vector Text -> Query Schema '[] (RowPG PostRowResponse)
199+getChannelPostsQ lim uIds tags = selectStar $ from (subquery $ postsQ' `As` #bar) &
200+ orderBy [#postRowUpdateTime & Desc] & limit lim
202+ unionWithUsers = case idsToColumn uIds of
203+ Just uIdsQ -> selectDotStar #posts' (from $ subquery (uIdsQ `As` #uIds) &
204+ innerJoin (subquery $ getPostQ id id `As` #posts')
205+ (#uIds ! #id .== #posts' ! #postRowAuthorId)) &
208+ postsQ' = unionWithUsers $ getPostQ id $ where_ $ jsonbLit tags .?| #postRowTags
150-- MARK: FrontEnd Data Structures
211-- MARK: FrontEnd Data Structures
@@ -156,6 +217,44 @@ data PostResponse = PostResponse {
157instance ToJSON PostResponse
218instance ToJSON PostResponse
220+data NamedChannelCreation = NamedChannelCreation {
221+ namedChannelCreationOwner :: UserId,
222+ namedChannelCreationName :: Text,
223+ namedChannelCreationTags :: Vector Text,
224+ namedChannelCreationPeopleIds :: Vector UserId
225+} deriving GHC.Generic
227+instance SOP.Generic NamedChannelCreation
228+instance SOP.HasDatatypeInfo NamedChannelCreation
230+addUserToChannelCreate :: UserId -> NamedChannelCreationRequest -> NamedChannelCreation
231+addUserToChannelCreate uId (NamedChannelCreationRequest cName cTags cPeople) = NamedChannelCreation uId cName cTags cPeople
234+data NamedChannelFull = NamedChannelFull {
235+ namedChannelFullId :: NamedChannelId,
236+ namedChannelFullOwner :: UserId,
237+ namedChannelFullName :: Text,
238+ namedChannelFullTags :: Vector Text,
239+ namedChannelFullPeopleIds :: Vector UserId
240+} deriving GHC.Generic
242+instance SOP.Generic NamedChannelFull
243+instance SOP.HasDatatypeInfo NamedChannelFull
245+addUserToChannelUpdate :: UserId -> NamedChannel UserId -> NamedChannelFull
246+addUserToChannelUpdate uId (NamedChannel cId cName cTags cPeople) = NamedChannelFull cId uId cName cTags cPeople
248+removeUserFromChannel :: NamedChannelFull -> NamedChannel UserId
249+removeUserFromChannel (NamedChannelFull cId _ cName cTags cPeople) = NamedChannel cId cName cTags cPeople
251+getChannelForUser :: UserId -> NamedChannelId -> StaticPQ NamedChannelFull
252+getChannelForUser uId cId = do
253+ channel <- runQueryParams getChannelForUserQ (uId, cId) >>= firstRow
254+ case (channel :: Maybe NamedChannelFull) of
255+ (Just c) -> return c
256+ Nothing -> lift $ S.throwError S.err404
160-- MARK: FrontEnd functions
259-- MARK: FrontEnd functions
@@ -183,16 +282,43 @@ getUser uId = runQueryParams getUserQ (Only uId) >>= getRow 0
183getUsers :: [UserId] -> StaticPQ (Maybe [User])
282getUsers :: [UserId] -> StaticPQ (Maybe [User])
184getUsers ids = getUsersQ ids `liftMaybe` (fmap Just . (runQuery >=> getRows))
283getUsers ids = getUsersQ ids `liftMaybe` (fmap Just . (runQuery >=> getRows))
186−publishPost :: UserId -> [PostElement Post] -> StaticPQ PostId
285+publishPost :: UserId -> PostCreation -> StaticPQ PostId
187−publishPost uId els = transactionally_ $ do
286+publishPost uId pc = transactionally_ $ do
188− pId' <- manipulateParams createPostRowQ (Only uId) >>= firstRow
287+ pId' <- manipulateParams createPostRowQ (uId, postCreationTags pc) >>= firstRow
189 case pId' of
288 case pId' of
190 (Just (Only pId)) -> do
289 (Just (Only pId)) -> do
191− traversePrepared_ createPostElementRowsQ $ elementsToRows pId els
290+ traversePrepared_ createPostElementRowsQ $ elementsToRows pId (postCreationBody pc)
192 return pId
291 return pId
193 _ -> lift $ S.throwError S.err404
292 _ -> lift $ S.throwError S.err404
294+createNewChannel :: UserId -> NamedChannelCreationRequest -> StaticPQ (Maybe NamedChannelId)
295+createNewChannel uId req = transactionally_ $ do
296+ cId <- manipulateParams createNamedChannelQ (addUserToChannelCreate uId req) >>= firstRow
297+ return $ fmap fromOnly cId
299+updateChannel :: UserId -> NamedChannel UserId -> StaticPQ ()
300+updateChannel uId req = transactionally_ $ do
301+ _ <- getChannelForUser uId $ namedChannelId req
302+ _ <- manipulateParams updateNamedChannelQ $ addUserToChannelUpdate uId req
305+getChannelPosts :: UserId -> Limit -> NamedChannelId -> StaticPQ PostResponse
306+getChannelPosts uId lim cId = do
307+ channel <- getChannelForUser uId cId
308+ postRows <- runQuery (getChannelPostsQ lim (toList $ namedChannelFullPeopleIds channel) (namedChannelFullTags channel))
310+ fmap (fromMaybe $ PostResponse mempty mempty) $ rowsToPosts postRows `liftMaybe` wrapIntoPostResponse
312+getAnonymousChannelPosts :: Limit -> AnonymousChannel -> StaticPQ PostResponse
313+getAnonymousChannelPosts lim (AnonymousChannel tags people) = do
314+ postRows <- runQuery (getChannelPostsQ lim people tags) >>= getRows
315+ fmap (fromMaybe $ PostResponse mempty mempty) $ rowsToPosts postRows `liftMaybe` wrapIntoPostResponse
317+getAllChannels :: UserId -> StaticPQ [NamedChannel UserId]
318+getAllChannels uId = fmap (map removeUserFromChannel) $
319+ runQueryParams getAllChannelsForUserQ (Only uId) >>= getRows
196-- MARK: Utils
322-- MARK: Utils
198foldMaybe :: [Maybe a] -> Maybe [a]
324foldMaybe :: [Maybe a] -> Maybe [a]