@@ -20,7 +20,12 @@ module Data.Query (
2020 getPostsForUser,
2121 Limit,
2222 publishPost,
23− PostResponse(..)
23+ PostResponse(..),
24+ getChannelPosts,
25+ updateChannel,
26+ createNewChannel,
27+ getAllChannels,
28+ getAnonymousChannelPosts
2429) where
2530
2631import Squeal.PostgreSQL
@@ -37,12 +42,20 @@ import Data.Word (Word64)
3742import qualified Data.List as L
3843import qualified Data.Map.Strict as M
3944import qualified Data.Set as Set
45+import qualified Generics.SOP as SOP
46+import qualified GHC.Generics as GHC
4047import Data.Int (Int64)
4148import Data.ByteString.Char8 (pack)
4249import GHC.Generics (Generic)
4350import Data.Aeson
4451import Servant.Docs
4552import 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)
4659
4760-- MARK: Documentation
4861
@@ -68,6 +81,7 @@ getPostQ oTe te = select (
6881 #posts ! #postRowId :*
6982 #posts ! #postRowAuthorId :*
7083 #posts ! #postRowUpdateTime :*
84+ #posts ! #postRowTags :*
7185 -- #rowElementId :*
7286 #postElements ! #rowElementOrd :*
7387 #postElements ! #rowElementMarkdown :*
@@ -128,11 +142,12 @@ getLastNPostsForUserQ lim = getPostQ
128142 (limit lim . orderBy [#posts ! #postRowUpdateTime & Desc])
129143 (where_ $ #postRowAuthorId .== param @1)
130144
131−createPostRowQ :: Manipulation Schema (TuplePG (Only UserId)) (RowPG (Only PostId))
145+createPostRowQ :: Manipulation Schema (TuplePG (UserId, Vector Text)) (RowPG (Only PostId))
132146createPostRowQ = insertRow #posts
133147 (Default `as` #postRowId :*
134148 Set (param @1) `as` #postRowAuthorId :*
135− Set currentTimestamp `as` #postRowUpdateTime)
149+ Set currentTimestamp `as` #postRowUpdateTime :*
150+ Set (param @2) `as` #postRowTags)
136151 OnConflictDoRaise
137152 (Returning $ #postRowId `as` #fromOnly)
138153
@@ -146,6 +161,52 @@ createPostElementRowsQ = insertRow_ #postElements
146161 Set (param @6) `as` #rowElementQuote :*
147162 Set (param @7) `as` #rowElementAttachment)
148163
164+getChannelForUserQ :: Query Schema (TuplePG (UserId, NamedChannelId)) (RowPG NamedChannelFull)
165+getChannelForUserQ = selectStar $
166+ from (table #channels) &
167+ where_ (#namedChannelFullOwner .== param @1 .&& #namedChannelFullId .== param @2)
168+
169+getAllChannelsForUserQ :: Query Schema (TuplePG (Only UserId)) (RowPG NamedChannelFull)
170+getAllChannelsForUserQ = selectStar $
171+ from (table #channels) &
172+ where_ (#namedChannelFullOwner .== param @1)
173+
174+createNamedChannelQ :: Manipulation Schema (TuplePG NamedChannelCreation) (RowPG (Only NamedChannelId))
175+createNamedChannelQ = insertRow #channels
176+ (
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
182+ )
183+ OnConflictDoRaise
184+ (Returning $ #namedChannelFullId `as` #fromOnly)
185+
186+updateNamedChannelQ :: Manipulation Schema (TuplePG NamedChannelFull) '[]
187+updateNamedChannelQ = update_ #channels
188+ (
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
194+ )
195+ (#namedChannelFullOwner .== param @2 .&& #namedChannelFullId .== param @2)
196+
197+
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
201+ where
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)) &
206+ union
207+ Nothing -> id
208+ postsQ' = unionWithUsers $ getPostQ id $ where_ $ jsonbLit tags .?| #postRowTags
209+
149210
150211-- MARK: FrontEnd Data Structures
151212
@@ -156,6 +217,44 @@ data PostResponse = PostResponse {
156217
157218instance ToJSON PostResponse
158219
220+data NamedChannelCreation = NamedChannelCreation {
221+ namedChannelCreationOwner :: UserId,
222+ namedChannelCreationName :: Text,
223+ namedChannelCreationTags :: Vector Text,
224+ namedChannelCreationPeopleIds :: Vector UserId
225+} deriving GHC.Generic
226+
227+instance SOP.Generic NamedChannelCreation
228+instance SOP.HasDatatypeInfo NamedChannelCreation
229+
230+addUserToChannelCreate :: UserId -> NamedChannelCreationRequest -> NamedChannelCreation
231+addUserToChannelCreate uId (NamedChannelCreationRequest cName cTags cPeople) = NamedChannelCreation uId cName cTags cPeople
232+
233+
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
241+
242+instance SOP.Generic NamedChannelFull
243+instance SOP.HasDatatypeInfo NamedChannelFull
244+
245+addUserToChannelUpdate :: UserId -> NamedChannel UserId -> NamedChannelFull
246+addUserToChannelUpdate uId (NamedChannel cId cName cTags cPeople) = NamedChannelFull cId uId cName cTags cPeople
247+
248+removeUserFromChannel :: NamedChannelFull -> NamedChannel UserId
249+removeUserFromChannel (NamedChannelFull cId _ cName cTags cPeople) = NamedChannel cId cName cTags cPeople
250+
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
257+
159258
160259-- MARK: FrontEnd functions
161260
@@ -183,16 +282,43 @@ getUser uId = runQueryParams getUserQ (Only uId) >>= getRow 0
183282getUsers :: [UserId] -> StaticPQ (Maybe [User])
184283getUsers ids = getUsersQ ids `liftMaybe` (fmap Just . (runQuery >=> getRows))
185284
186−publishPost :: UserId -> [PostElement Post] -> StaticPQ PostId
187−publishPost uId els = transactionally_ $ do
188− pId' <- manipulateParams createPostRowQ (Only uId) >>= firstRow
285+publishPost :: UserId -> PostCreation -> StaticPQ PostId
286+publishPost uId pc = transactionally_ $ do
287+ pId' <- manipulateParams createPostRowQ (uId, postCreationTags pc) >>= firstRow
189288 case pId' of
190289 (Just (Only pId)) -> do
191− traversePrepared_ createPostElementRowsQ $ elementsToRows pId els
290+ traversePrepared_ createPostElementRowsQ $ elementsToRows pId (postCreationBody pc)
192291 return pId
193292 _ -> lift $ S.throwError S.err404
194293
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
298+
299+updateChannel :: UserId -> NamedChannel UserId -> StaticPQ ()
300+updateChannel uId req = transactionally_ $ do
301+ _ <- getChannelForUser uId $ namedChannelId req
302+ _ <- manipulateParams updateNamedChannelQ $ addUserToChannelUpdate uId req
303+ return ()
304+
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))
309+ >>= getRows
310+ fmap (fromMaybe $ PostResponse mempty mempty) $ rowsToPosts postRows `liftMaybe` wrapIntoPostResponse
311+
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
316+
317+getAllChannels :: UserId -> StaticPQ [NamedChannel UserId]
318+getAllChannels uId = fmap (map removeUserFromChannel) $
319+ runQueryParams getAllChannelsForUserQ (Only uId) >>= getRows
195320
321+
196322-- MARK: Utils
197323
198324foldMaybe :: [Maybe a] -> Maybe [a]