on master help sign in
Actually resolved #1.
2158807iko
denis/src/Data/Post.hs+10−13
@@ -10,12 +10,6 @@
10module Data.Post (
10module Data.Post (
11 Post(..),
11 Post(..),
12 PostElement,
12 PostElement,
13− -- PostQuote(..),
14− -- PostRow,
15− -- PostQuoteRow,
16− -- ElementRow,
17− postToRows,
18− -- postToElementRows,
19 rowsToPost,
13 rowsToPost,
20 PostId,
14 PostId,
21 QuoteId,
15 QuoteId,
@@ -31,11 +25,14 @@ import Data.Proxy
31import Data.PostElement
25import Data.PostElement
32import Data.Text (Text)
26import Data.Text (Text)
33import Data.Function (on)
27import Data.Function (on)
28+import Data.Time.Clock
29+import Data.Time.Calendar
34
30
35-- MARK: Documentation
31-- MARK: Documentation
36
32
37instance ToSample Post where
33instance ToSample Post where
38− toSamples _ = samples $ [Post 8 17] <*> (map snd $ toSamples Proxy)
34+ toSamples _ = samples $ [Post 8 17 time] <*> (map snd $ toSamples Proxy)
35+ where time = UTCTime (ModifiedJulianDay 1000) 8
39
36
40
37
41-- MARK: Actual type
38-- MARK: Actual type
@@ -46,13 +43,15 @@ type QuoteId = Int64
46data Post = Post {
43data Post = Post {
47 postId :: Int64,
44 postId :: Int64,
48 postAuthorId :: Int64,
45 postAuthorId :: Int64,
46+ updated :: UTCTime,
49 postBody :: [PostElement Post]
47 postBody :: [PostElement Post]
50}
48}
51
49
52instance ToJSON Post where
50instance ToJSON Post where
53− toJSON (Post pId aId pb) = object [
51+ toJSON (Post pId aId tm pb) = object [
54 "id" .= pId,
52 "id" .= pId,
55 "authorId" .= aId,
53 "authorId" .= aId,
54+ "updated" .= tm,
56 "body" .= pb ]
55 "body" .= pb ]
57
56
58
57
@@ -64,6 +63,7 @@ data PostRowResponse = PostRowResponse {
64 -- PostRow
63 -- PostRow
65 postRowId :: Int64,
64 postRowId :: Int64,
66 postRowAuthorId :: Int64,
65 postRowAuthorId :: Int64,
66+ postRowUpdateTime :: UTCTime,
67
67
68 -- ElementRow
68 -- ElementRow
69 rowElementOrd :: Int64,
69 rowElementOrd :: Int64,
@@ -78,13 +78,10 @@ instance SOP.Generic PostRowResponse
78instance SOP.HasDatatypeInfo PostRowResponse
78instance SOP.HasDatatypeInfo PostRowResponse
79
79
80unRow :: MkElementRow x -> PostRowResponse -> x
80unRow :: MkElementRow x -> PostRowResponse -> x
81−unRow f' (PostRowResponse _ _ _ a b c d e) = f' a b c d e
81+unRow f' (PostRowResponse _ _ _ _ a b c d e) = f' a b c d e
82−
83−postToRows :: Post -> [PostRowResponse]
84−postToRows (Post pId uId els) = map (\(el, f) -> elemenToRow f el) . zip els . map (PostRowResponse pId uId) $ [0..]
85
82
86rowsToPost :: [PostRowResponse] -> Maybe Post
83rowsToPost :: [PostRowResponse] -> Maybe Post
87rowsToPost [] = Nothing
84rowsToPost [] = Nothing
88rowsToPost (el:els) = if all (((==) `on` postRowId) el) els
85rowsToPost (el:els) = if all (((==) `on` postRowId) el) els
89− then Post (postRowId el) (postRowAuthorId el) <$> traverse (unRow rowsToElement) (el:els)
86+ then Post (postRowId el) (postRowAuthorId el) (postRowUpdateTime el) <$> traverse (unRow rowsToElement) (el:els)
90 else Nothing
87 else Nothing
denis/src/Data/PostElement.hs−14
@@ -42,20 +42,6 @@ data PostElement a = Markdown Text
42 | Quote PostQuote
42 | Quote PostQuote
43 | Attachment Text
43 | Attachment Text
44
44
45−
46−-- data ElementRow a = ElementRow {
47−-- rowElementId :: Int64, -- the source id
48−-- rowElementOrd :: Int64,
49−-- rowElementMarkdown :: Maybe Text,
50−-- rowElementLatex :: Maybe Text,
51−-- rowElementImage :: Maybe Text,
52−-- rowElementQuote :: Maybe Int64,
53−-- rowElementAttachment :: Maybe Text
54−-- } deriving (GHC.Generic)
55−
56−-- instance SOP.Generic (ElementRow a)
57−-- instance SOP.HasDatatypeInfo (ElementRow a)
58−
59instance ToJSON (PostElement t) where
45instance ToJSON (PostElement t) where
60 toJSON (Markdown m) = object ["markdown" .= m]
46 toJSON (Markdown m) = object ["markdown" .= m]
61 toJSON (Latex l) = object ["latex" .= l]
47 toJSON (Latex l) = object ["latex" .= l]
denis/src/Data/Query.hs+12−10
@@ -51,6 +51,7 @@ getPostQ :: TableEndo Schema params _ 'Ungrouped -> TableEndo Schema params _ 'U
51getPostQ oTe te = select (
51getPostQ oTe te = select (
52 #posts ! #postRowId :*
52 #posts ! #postRowId :*
53 #posts ! #postRowAuthorId :*
53 #posts ! #postRowAuthorId :*
54+ #posts ! #postRowUpdateTime :*
54 -- #rowElementId :*
55 -- #rowElementId :*
55 #postElements ! #rowElementOrd :*
56 #postElements ! #rowElementOrd :*
56 #postElements ! #rowElementMarkdown :*
57 #postElements ! #rowElementMarkdown :*
@@ -92,29 +93,30 @@ type Limit = Word64
92
93
93getLastNPostsQ :: Limit -> Query Schema params (RowPG PostRowResponse)
94getLastNPostsQ :: Limit -> Query Schema params (RowPG PostRowResponse)
94getLastNPostsQ lim = getPostQ
95getLastNPostsQ lim = getPostQ
95− (limit lim . orderBy [#posts ! #postRowId & Desc]) id
96+ (limit lim . orderBy [#posts ! #postRowUpdateTime & Desc]) id
96
97
97getLastNPostsForUserQ :: Limit -> Query Schema (TuplePG (Only UserId)) (RowPG PostRowResponse)
98getLastNPostsForUserQ :: Limit -> Query Schema (TuplePG (Only UserId)) (RowPG PostRowResponse)
98getLastNPostsForUserQ lim = getPostQ
99getLastNPostsForUserQ lim = getPostQ
99− (limit lim . orderBy [#posts ! #postRowId & Desc])
100+ (limit lim . orderBy [#posts ! #postRowUpdateTime & Desc])
100 (where_ $ #postRowAuthorId .== param @1)
101 (where_ $ #postRowAuthorId .== param @1)
101
102
102createPostRowQ :: Manipulation Schema (TuplePG (Only UserId)) (RowPG (Only PostId))
103createPostRowQ :: Manipulation Schema (TuplePG (Only UserId)) (RowPG (Only PostId))
103createPostRowQ = insertRow #posts
104createPostRowQ = insertRow #posts
105+ (Default `as` #postRowId :*
104− (Default `as` #postRowId :* Set (param @1) `as` #postRowAuthorId)
106+ Set (param @1) `as` #postRowAuthorId :*
107+ Set currentTimestamp `as` #postRowUpdateTime)
105 OnConflictDoRaise
108 OnConflictDoRaise
106 (Returning $ #postRowId `as` #fromOnly)
109 (Returning $ #postRowId `as` #fromOnly)
107
110
108createPostElementRowsQ :: Manipulation Schema (TuplePG (PostElementRow Post)) '[]
111createPostElementRowsQ :: Manipulation Schema (TuplePG (PostElementRow Post)) '[]
109createPostElementRowsQ = insertRow_ #postElements
112createPostElementRowsQ = insertRow_ #postElements
110 (Set (param @1) `as` #rowElementId :*
113 (Set (param @1) `as` #rowElementId :*
111− Set (param @2) `as` #rowElementAuthorId :*
114+ Set (param @2) `as` #rowElementOrd :*
112− Set (param @3) `as` #rowElementOrd :*
115+ Set (param @3) `as` #rowElementMarkdown :*
113− Set (param @4) `as` #rowElementMarkdown :*
116+ Set (param @4) `as` #rowElementLatex :*
114− Set (param @5) `as` #rowElementLatex :*
117+ Set (param @5) `as` #rowElementImage :*
115− Set (param @6) `as` #rowElementImage :*
118+ Set (param @6) `as` #rowElementQuote :*
116− Set (param @7) `as` #rowElementQuote :*
117− Set (param @8) `as` #rowElementAttachment)
119+ Set (param @7) `as` #rowElementAttachment)
118
120
119-- MARK: FrontEnd functions
121-- MARK: FrontEnd functions
120
122
denis/src/Data/Schema.hs+4−10
@@ -30,7 +30,8 @@ type Schema =
30 "fk_author_id" ::: 'ForeignKey '["postRowAuthorId"] "users" '["userId"]] :=>
30 "fk_author_id" ::: 'ForeignKey '["postRowAuthorId"] "users" '["userId"]] :=>
31 '[
31 '[
32 "postRowId" ::: 'Def :=> 'NotNull 'PGint8,
32 "postRowId" ::: 'Def :=> 'NotNull 'PGint8,
33− "postRowAuthorId" ::: 'NoDef :=> 'NotNull 'PGint8
33+ "postRowAuthorId" ::: 'NoDef :=> 'NotNull 'PGint8,
34+ "postRowUpdateTime" ::: 'NoDef :=> 'NotNull 'PGtimestamptz
34 ]),
35 ]),
35 "quotes" ::: 'Table (
36 "quotes" ::: 'Table (
36 '[
37 '[
@@ -43,14 +44,12 @@ type Schema =
43 "postElements" ::: 'Table (
44 "postElements" ::: 'Table (
44 '[
45 '[
45 "fk_post_quote_self_id" ::: 'ForeignKey '["rowElementId"] "posts" '["postRowId"],
46 "fk_post_quote_self_id" ::: 'ForeignKey '["rowElementId"] "posts" '["postRowId"],
46− "fk_post_element_user_id" ::: 'ForeignKey '["rowElementAuthorId"] "users" '["userId"],
47 "fk_post_quote_id" ::: 'ForeignKey '["rowElementQuote"] "quotes" '["quoteRowId"]
47 "fk_post_quote_id" ::: 'ForeignKey '["rowElementQuote"] "quotes" '["quoteRowId"]
48 -- ,
48 -- ,
49 -- "unique_post_quote" ::: 'Check '["rowElementOrd", "rowElementMarkdown", "rowElementLatex", "rowElementImage", "rowElementQuote", "rowElementAttachment"]
49 -- "unique_post_quote" ::: 'Check '["rowElementOrd", "rowElementMarkdown", "rowElementLatex", "rowElementImage", "rowElementQuote", "rowElementAttachment"]
50 ] :=>
50 ] :=>
51 '[
51 '[
52 "rowElementId" ::: 'NoDef :=> 'NotNull 'PGint8,
52 "rowElementId" ::: 'NoDef :=> 'NotNull 'PGint8,
53− "rowElementAuthorId" ::: 'NoDef :=> 'NotNull 'PGint8,
54 "rowElementOrd" ::: 'NoDef :=> 'NotNull 'PGint8,
53 "rowElementOrd" ::: 'NoDef :=> 'NotNull 'PGint8,
55 "rowElementMarkdown" ::: 'NoDef :=> 'Null 'PGtext,
54 "rowElementMarkdown" ::: 'NoDef :=> 'Null 'PGtext,
56 "rowElementLatex" ::: 'NoDef :=> 'Null 'PGtext,
55 "rowElementLatex" ::: 'NoDef :=> 'Null 'PGtext,
@@ -61,14 +60,12 @@ type Schema =
61 "quoteElements" ::: 'Table (
60 "quoteElements" ::: 'Table (
62 '[
61 '[
63 "fk_quotes_quote_self_id" ::: 'ForeignKey '["rowElementId"] "quotes" '["quoteRowId"],
62 "fk_quotes_quote_self_id" ::: 'ForeignKey '["rowElementId"] "quotes" '["quoteRowId"],
64− "fk_quote_element_user_id" ::: 'ForeignKey '["rowElementAuthorId"] "users" '["userId"],
65 "fk_quote_quote_id" ::: 'ForeignKey '["rowElementQuote"] "quotes" '["quoteRowId"]
63 "fk_quote_quote_id" ::: 'ForeignKey '["rowElementQuote"] "quotes" '["quoteRowId"]
66 -- ,
64 -- ,
67 -- "unique_quote_quote" ::: 'Check '["rowElementOrd", "rowElementMarkdown", "rowElementLatex", "rowElementImage", "rowElementQuote", "rowElementAttachment"]
65 -- "unique_quote_quote" ::: 'Check '["rowElementOrd", "rowElementMarkdown", "rowElementLatex", "rowElementImage", "rowElementQuote", "rowElementAttachment"]
68 ] :=>
66 ] :=>
69 '[
67 '[
70 "rowElementId" ::: 'NoDef :=> 'NotNull 'PGint8,
68 "rowElementId" ::: 'NoDef :=> 'NotNull 'PGint8,
71− "rowElementAuthorId" ::: 'NoDef :=> 'NotNull 'PGint8,
72 "rowElementOrd" ::: 'NoDef :=> 'NotNull 'PGint8,
69 "rowElementOrd" ::: 'NoDef :=> 'NotNull 'PGint8,
73 "rowElementMarkdown" ::: 'NoDef :=> 'Null 'PGtext,
70 "rowElementMarkdown" ::: 'NoDef :=> 'Null 'PGtext,
74 "rowElementLatex" ::: 'NoDef :=> 'Null 'PGtext,
71 "rowElementLatex" ::: 'NoDef :=> 'Null 'PGtext,
@@ -101,7 +98,8 @@ createTables = createTable #users (
101 primaryKey #userId `as` #pk_users
98 primaryKey #userId `as` #pk_users
102 ) >>> createTable #posts (
99 ) >>> createTable #posts (
103 serial8 `as` #postRowId :*
100 serial8 `as` #postRowId :*
104− notNullable int8 `as` #postRowAuthorId
101+ notNullable int8 `as` #postRowAuthorId :*
102+ notNullable timestampWithTimeZone `as` #postRowUpdateTime
105 ) (
103 ) (
106 primaryKey #postRowId `as` #pk_posts :*
104 primaryKey #postRowId `as` #pk_posts :*
107 foreignKey #postRowAuthorId #users #userId OnDeleteCascade OnUpdateCascade `as` #fk_author_id
105 foreignKey #postRowAuthorId #users #userId OnDeleteCascade OnUpdateCascade `as` #fk_author_id
@@ -113,7 +111,6 @@ createTables = createTable #users (
113 foreignKey #quoteRowPostId #posts #postRowId OnDeleteCascade OnUpdateCascade `as` #fk_quote_post_id
111 foreignKey #quoteRowPostId #posts #postRowId OnDeleteCascade OnUpdateCascade `as` #fk_quote_post_id
114 ) >>> createTable #postElements (
112 ) >>> createTable #postElements (
115 notNullable int8 `as` #rowElementId :*
113 notNullable int8 `as` #rowElementId :*
116− notNullable int8 `as` #rowElementAuthorId :*
117 notNullable int8 `as` #rowElementOrd :*
114 notNullable int8 `as` #rowElementOrd :*
118 nullable text `as` #rowElementMarkdown :*
115 nullable text `as` #rowElementMarkdown :*
119 nullable text `as` #rowElementLatex :*
116 nullable text `as` #rowElementLatex :*
@@ -122,11 +119,9 @@ createTables = createTable #users (
122 nullable text `as` #rowElementAttachment
119 nullable text `as` #rowElementAttachment
123 ) (
120 ) (
124 foreignKey #rowElementId #posts #postRowId OnDeleteCascade OnUpdateCascade `as` #fk_post_quote_self_id :*
121 foreignKey #rowElementId #posts #postRowId OnDeleteCascade OnUpdateCascade `as` #fk_post_quote_self_id :*
125− foreignKey #rowElementAuthorId #users #userId OnDeleteCascade OnUpdateCascade `as` #fk_post_element_user_id :*
126 foreignKey #rowElementQuote #quotes #quoteRowId OnDeleteCascade OnUpdateCascade `as` #fk_post_quote_id
122 foreignKey #rowElementQuote #quotes #quoteRowId OnDeleteCascade OnUpdateCascade `as` #fk_post_quote_id
127 ) >>> createTable #quoteElements (
123 ) >>> createTable #quoteElements (
128 notNullable int8 `as` #rowElementId :*
124 notNullable int8 `as` #rowElementId :*
129− notNullable int8 `as` #rowElementAuthorId :*
130 notNullable int8 `as` #rowElementOrd :*
125 notNullable int8 `as` #rowElementOrd :*
131 nullable text `as` #rowElementMarkdown :*
126 nullable text `as` #rowElementMarkdown :*
132 nullable text `as` #rowElementLatex :*
127 nullable text `as` #rowElementLatex :*
@@ -135,7 +130,6 @@ createTables = createTable #users (
135 nullable text `as` #rowElementAttachment
130 nullable text `as` #rowElementAttachment
136 ) (
131 ) (
137 foreignKey #rowElementId #quotes #quoteRowId OnDeleteCascade OnUpdateCascade `as` #fk_quotes_quote_self_id :*
132 foreignKey #rowElementId #quotes #quoteRowId OnDeleteCascade OnUpdateCascade `as` #fk_quotes_quote_self_id :*
138− foreignKey #rowElementAuthorId #users #userId OnDeleteCascade OnUpdateCascade `as` #fk_quote_element_user_id :*
139 foreignKey #rowElementQuote #quotes #quoteRowId OnDeleteCascade OnUpdateCascade `as` #fk_quote_quote_id
133 foreignKey #rowElementQuote #quotes #quoteRowId OnDeleteCascade OnUpdateCascade `as` #fk_quote_quote_id
140 -- ) >>> createView #postsView (select (
134 -- ) >>> createView #postsView (select (
141 -- #posts ! #postRowId :*
135 -- #posts ! #postRowId :*
denis/src/Server/API.hs+2−6
@@ -16,7 +16,6 @@ import Data.Proxy
16import Servant.API
16import Servant.API
17import Data.User
17import Data.User
18import Server.Auth
18import Server.Auth
19−import Data.Int ()
20import Servant.Server
19import Servant.Server
21import Data.Connection
20import Data.Connection
22import Data.Query
21import Data.Query
@@ -32,6 +31,7 @@ instance HasDocs api => HasDocs (Authentication :> api) where
32 docsFor Proxy (endpoint, action) =
31 docsFor Proxy (endpoint, action) =
33 docsFor (Proxy :: Proxy api) (endpoint, action & notes <>~ [DocNote "Authentication" ["This method requires cookies set in the `POST /authenticate` method.\n\nReturns error `401 Unauthorized` if the cookies are invalid or don't premit access to the requested data."]])
32 docsFor (Proxy :: Proxy api) (endpoint, action & notes <>~ [DocNote "Authentication" ["This method requires cookies set in the `POST /authenticate` method.\n\nReturns error `401 Unauthorized` if the cookies are invalid or don't premit access to the requested data."]])
34
33
34+
35-- MARK: Implementation
35-- MARK: Implementation
36
36
37type Authentication = AuthProtect "basicAuth"
37type Authentication = AuthProtect "basicAuth"
@@ -53,8 +53,4 @@ mkServerAPI l =
53 authenticate :<|> (\uId ->
53 authenticate :<|> (\uId ->
54 mapM (runQnotFound . getUser) :<|>
54 mapM (runQnotFound . getUser) :<|>
55 postApi uId
55 postApi uId
56− )
56+ )
57−
58−-- getPostsS :: [UserId] -> App [User]
59−-- getPostsS = mapM getPost
60−