Skip to content
This repository was archived by the owner on Feb 6, 2024. It is now read-only.

Commit 428e344

Browse files
committed
feat: backend UserInfo
1 parent 0303592 commit 428e344

File tree

2 files changed

+121
-54
lines changed

2 files changed

+121
-54
lines changed

infra/handler/app/Test.hs

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ withServer act = do
3535
{ HTTPClient.managerModifyRequest =
3636
pure . rerouteDynamoDB
3737
}
38-
withPristineDB $ \(conn, _iface) -> do
38+
withPristineDB $ \conn -> do
3939
env <- Aws.newEnv Aws.Discover <&> Aws.envManager .~ mgr
4040

4141
(port, socket) <- Warp.openFreePort
@@ -50,14 +50,16 @@ withServer act = do
5050
Left () -> error "Server returned"
5151
Right a -> pure a
5252

53-
withPristineDB :: ((HC.Connection, DbInterface) -> IO a) -> IO a
53+
withPristineDB :: (HC.Connection -> IO a) -> IO a
5454
withPristineDB act = do
5555
conn <- getPostgresqlConnection
56+
putStrLn "DROP TABLE IF EXISTS username"
5657
void $ HS.run (HS.sql "DROP TABLE IF EXISTS username") conn
57-
void $ HS.run (HS.sql "DROP TABLE IF EXISTS account") conn
58+
putStrLn "DROP TABLE IF EXISTS account CASCADE"
59+
void $ HS.run (HS.sql "DROP TABLE IF EXISTS account CASCADE") conn
60+
putStrLn "DROP TABLE IF EXISTS db_meta"
5861
void $ HS.run (HS.sql "DROP TABLE IF EXISTS db_meta") conn
59-
iface <- getDbInterface conn
60-
act (conn, iface)
62+
act conn
6163

6264
main :: IO ()
6365
main = do
@@ -74,7 +76,8 @@ main = do
7476
]
7577

7678
testUsersGet :: IO ()
77-
testUsersGet = withPristineDB $ \(_, iface) -> do
79+
testUsersGet = withPristineDB $ \conn -> do
80+
iface <- getDbInterface conn
7881
dbGetAllUsers iface >>= \case
7982
[] -> pure ()
8083
users -> error $ "Expected no users, got: " <> show users
@@ -97,7 +100,8 @@ testUsersGet = withPristineDB $ \(_, iface) -> do
97100
users -> error $ "Expected no users, got: " <> show users
98101

99102
testUsersGetByUserId :: IO ()
100-
testUsersGetByUserId = withPristineDB $ \(_, iface) -> do
103+
testUsersGetByUserId = withPristineDB $ \conn -> do
104+
iface <- getDbInterface conn
101105
let someFirebaseId = FirebaseId "foo"
102106
someUserId = UserId someFirebaseId
103107
someUser = User
@@ -116,7 +120,8 @@ testUsersGetByUserId = withPristineDB $ \(_, iface) -> do
116120
Nothing -> error "Got no users"
117121

118122
testUsersDelete :: IO ()
119-
testUsersDelete = withPristineDB $ \(_, iface) -> do
123+
testUsersDelete = withPristineDB $ \conn -> do
124+
iface <- getDbInterface conn
120125
let someFirebaseId = FirebaseId "foo"
121126
someUserId = UserId someFirebaseId
122127
someUser = User
@@ -132,7 +137,8 @@ testUsersDelete = withPristineDB $ \(_, iface) -> do
132137
Right () -> pure ()
133138

134139
testUsersCreate :: IO ()
135-
testUsersCreate = withPristineDB $ \(_, iface) -> do
140+
testUsersCreate = withPristineDB $ \conn -> do
141+
iface <- getDbInterface conn
136142
let someFirebaseId = FirebaseId "foo"
137143
someUserId = UserId someFirebaseId
138144
someUser = User
@@ -144,7 +150,8 @@ testUsersCreate = withPristineDB $ \(_, iface) -> do
144150
Right () -> pure ()
145151

146152
testUsersUpdate :: IO ()
147-
testUsersUpdate = withPristineDB $ \(_, iface) -> do
153+
testUsersUpdate = withPristineDB $ \conn -> do
154+
iface <- getDbInterface conn
148155
let someFirebaseId = FirebaseId "foo"
149156
someUserId = UserId someFirebaseId
150157
someUser = User
@@ -258,16 +265,17 @@ main' = withServer $ \port -> do
258265
Right decks ->
259266
if decks == [] then pure () else (error $ "Expected no decks, got: " <> show decks)
260267

261-
let someUser = User
262-
{ userFirebaseId = someFirebaseId
263-
, userUsername = Just (Username "patrick") }
268+
let someUserInfo = UserInfo
269+
{ userInfoFirebaseId = someFirebaseId
270+
, userInfoEmail = Just "patrick" }
271+
someUser = userInfoToUser someUserInfo
264272

265-
runClientM (usersPost' b someUser) clientEnv >>= \case
273+
runClientM (usersPost' b someUserInfo) clientEnv >>= \case
266274
Left err -> error $ "Expected user, got error: " <> show err
267275
Right (Item userId user) ->
268276
if user == someUser && userId == someUserId then pure () else (error $ "Expected same user, got: " <> show user)
269277

270-
runClientM (usersPost' b someUser) clientEnv >>= \case
278+
runClientM (usersPost' b someUserInfo) clientEnv >>= \case
271279
Left (FailureResponse resp) ->
272280
if HTTP.statusCode (responseStatusCode resp) == 409 then pure () else
273281
error $ "Got unexpected response: " <> show resp
@@ -279,8 +287,8 @@ main' = withServer $ \port -> do
279287

280288
usersGet' :: ClientM [Item UserId User]
281289
_usersGetUserId' :: UserId -> ClientM (Item UserId User)
282-
usersPost' :: T.Text -> User -> ClientM (Item UserId User)
283-
_usersPut' :: T.Text -> UserId -> User -> ClientM (Item UserId User)
290+
usersPost' :: T.Text -> UserInfo -> ClientM (Item UserId User)
291+
_usersPut' :: T.Text -> UserId -> UserInfo -> ClientM (Item UserId User)
284292
_usersDelete' :: T.Text -> UserId -> ClientM ()
285293

286294
decksGet' :: T.Text -> Maybe UserId -> ClientM [Item DeckId Deck]

infra/handler/src/DeckGo/Handler.hs

Lines changed: 96 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE MultiWayIf #-}
23
{-# LANGUAGE MonadFailDesugaring #-}
34
{-# LANGUAGE RankNTypes #-}
45
{-# LANGUAGE DeriveGeneric #-}
@@ -93,20 +94,25 @@ type UsersAPI =
9394
Get '[JSON] [Item UserId User] :<|>
9495
Capture "user_id" UserId :> Get '[JSON] (Item UserId User) :<|>
9596
Protected :>
96-
ReqBody '[JSON] User :>
97+
ReqBody '[JSON] UserInfo :>
9798
Post '[JSON] (Item UserId User) :<|>
9899
Protected :>
99100
Capture "user_id" UserId :>
100-
ReqBody '[JSON] User :> Put '[JSON] (Item UserId User) :<|>
101+
ReqBody '[JSON] UserInfo :> Put '[JSON] (Item UserId User) :<|>
101102
Protected :> Capture "user_id" UserId :> Delete '[JSON] ()
102103

103104
newtype Username = Username { unUsername :: T.Text }
104105
deriving stock (Show, Eq)
105106
deriving newtype (Aeson.FromJSON, Aeson.ToJSON)
106107

108+
data UserInfo = UserInfo
109+
{ userInfoFirebaseId :: FirebaseId
110+
, userInfoEmail :: Maybe T.Text
111+
} deriving (Show, Eq)
112+
107113
data User = User
108114
{ userFirebaseId :: FirebaseId
109-
, userUsername :: Maybe Username
115+
, userUsername :: Maybe Username -- + return anonymous
110116
} deriving (Show, Eq)
111117

112118
newtype UserId = UserId { unUserId :: FirebaseId }
@@ -134,6 +140,21 @@ newtype FirebaseId = FirebaseId { unFirebaseId :: T.Text }
134140
( Generic )
135141

136142
-- XXX !!?!??!?!! pattern match failures are propagated to the client!!!
143+
instance FromJSONObject UserInfo where
144+
parseJSONObject = \obj ->
145+
UserInfo
146+
<$> obj .: "firebase_uid"
147+
<*> (
148+
(do
149+
True <- obj .: "anonymous"
150+
(Nothing :: Maybe T.Text) <- obj .:? "email"
151+
pure Nothing
152+
) <|> (do
153+
False <- obj .: "anonymous"
154+
obj .:? "email"
155+
)
156+
)
157+
137158
instance FromJSONObject User where
138159
parseJSONObject = \obj ->
139160
User
@@ -145,23 +166,33 @@ instance FromJSONObject User where
145166
pure Nothing
146167
) <|> (do
147168
False <- obj .: "anonymous"
148-
obj .: "username"
169+
obj .:? "username"
149170
)
150171
)
151172

173+
instance ToJSONObject UserInfo where
174+
toJSONObject uinfo = HMS.fromList
175+
[ "anonymous" .= isNothing (userInfoEmail uinfo)
176+
, "email" .= userInfoEmail uinfo
177+
, "firebase_uid" .= userInfoFirebaseId uinfo
178+
]
179+
180+
instance Aeson.FromJSON UserInfo where
181+
parseJSON = Aeson.withObject "UserInfo" parseJSONObject
182+
152183
instance ToJSONObject User where
153184
toJSONObject user = HMS.fromList
154-
[ "firebase_uid" .= userFirebaseId user
155-
, "anonymous" .= isNothing (userUsername user)
185+
[ "anonymous" .= isNothing (userUsername user)
156186
, "username" .= userUsername user
187+
, "firebase_uid" .= userFirebaseId user
157188
]
158189

159-
instance Aeson.FromJSON User where
160-
parseJSON = Aeson.withObject "User" parseJSONObject
161-
162190
instance Aeson.ToJSON User where
163191
toJSON = Aeson.Object . toJSONObject
164192

193+
instance Aeson.ToJSON UserInfo where
194+
toJSON = Aeson.Object . toJSONObject
195+
165196
instance ToSchema (Item UserId User) where
166197
declareNamedSchema _ = pure $ NamedSchema (Just "UserWithId") mempty
167198

@@ -174,6 +205,18 @@ instance ToParamSchema (Item UserId User) where
174205
instance ToParamSchema UserId where
175206
toParamSchema _ = mempty
176207

208+
-- instance ToSchema (Item UserId User) where
209+
-- declareNamedSchema _ = pure $ NamedSchema (Just "UserWithId") mempty
210+
211+
instance ToSchema UserInfo where
212+
declareNamedSchema _ = pure $ NamedSchema (Just "UserInfo") mempty
213+
214+
instance ToParamSchema (Item UserId UserInfo) where
215+
toParamSchema _ = mempty
216+
217+
-- instance ToParamSchema UserId where
218+
-- toParamSchema _ = mempty
219+
177220
-- DECKS
178221

179222
type DecksAPI =
@@ -415,21 +458,34 @@ usersGetUserIdStatement = Statement sql encoder decoder True
415458
HD.nullableColumn (Username <$> HD.text)
416459
)
417460

418-
usersPost :: HC.Connection -> Firebase.UserId -> User -> Servant.Handler (Item UserId User)
419-
usersPost conn fuid user = do
420-
let userId = UserId (userFirebaseId user)
421-
liftIO $ putStrLn "POST users"
461+
usersPost
462+
:: HC.Connection
463+
-> Firebase.UserId
464+
-> UserInfo
465+
-> Servant.Handler (Item UserId User)
466+
usersPost conn fuid uinfo = do
422467

423-
when (Firebase.unUserId fuid /= unFirebaseId (userFirebaseId user)) $ do
468+
when (Firebase.unUserId fuid /= unFirebaseId (userInfoFirebaseId uinfo)) $ do
424469
Servant.throwError Servant.err403
425-
liftIO $ putStrLn "auth is ok"
426470

427471
iface <- liftIO $ getDbInterface conn
428472
liftIO $ putStrLn "got DB interface"
473+
474+
let userId = UserId (userInfoFirebaseId uinfo)
475+
user = userInfoToUser uinfo
429476
liftIO (dbCreateUser iface userId user) >>= \case
430477
Left () -> Servant.throwError $ Servant.err409
431478
Right () -> pure $ Item userId user
432479

480+
userInfoToUser :: UserInfo -> User
481+
userInfoToUser uinfo = User
482+
{ userFirebaseId = userInfoFirebaseId uinfo
483+
, userUsername = emailToUsername <$> userInfoEmail uinfo
484+
}
485+
486+
emailToUsername :: T.Text -> Username
487+
emailToUsername = Username
488+
433489
usersPostSession :: UserId -> User -> HS.Session (Either () ())
434490
usersPostSession uid u = do
435491
HS.sql "BEGIN"
@@ -505,20 +561,26 @@ usersPostStatement'' = Statement sql encoder decoder True
505561
(HE.param HE.text)
506562
decoder = HD.unit
507563

508-
usersPut :: HC.Connection -> Firebase.UserId -> UserId -> User -> Servant.Handler (Item UserId User)
509-
usersPut conn fuid userId user = do
564+
usersPut
565+
:: HC.Connection
566+
-> Firebase.UserId
567+
-> UserId
568+
-> UserInfo
569+
-> Servant.Handler (Item UserId User)
570+
usersPut conn fuid userId uinfo = do
510571

511572
when (Firebase.unUserId fuid /= unFirebaseId (unUserId userId)) $ do
512573
liftIO $ putStrLn $ unwords
513-
[ "User is trying to update another user:", show (fuid, userId, user) ]
574+
[ "User is trying to update another uinfo:", show (fuid, userId, uinfo) ]
514575
Servant.throwError Servant.err404
515576

516-
when (Firebase.unUserId fuid /= unFirebaseId (userFirebaseId user)) $ do
577+
when (Firebase.unUserId fuid /= unFirebaseId (userInfoFirebaseId uinfo)) $ do
517578
liftIO $ putStrLn $ unwords
518-
[ "Client used the wrong user ID on user", show (fuid, userId, user) ]
579+
[ "Client used the wrong uinfo ID on uinfo", show (fuid, userId, uinfo) ]
519580
Servant.throwError Servant.err400
520581

521582
iface <- liftIO $ getDbInterface conn
583+
let user = userInfoToUser uinfo
522584
liftIO (dbUpdateUser iface userId user) >>= \case
523585
UserUpdateOk -> pure $ Item userId user -- TODO: check # of affected rows
524586
e -> do -- TODO: handle not found et al.
@@ -1131,12 +1193,9 @@ data DbVersion
11311193

11321194
-- | Migrates from ver to latest
11331195
migrateFrom :: DbVersion -> HS.Session ()
1134-
migrateFrom = \ver ->
1135-
if ver < maxBound
1136-
then
1137-
let frm = succ ver
1138-
in forM_ [frm .. maxBound] migrateTo
1139-
else pure ()
1196+
migrateFrom = \ver -> do
1197+
liftIO $ putStrLn $ "Migration: " <> show (dbVersionToText <$> [ver ..maxBound])
1198+
forM_ [ver .. maxBound] migrateTo
11401199
where
11411200
-- | Migrates from (ver -1) to ver
11421201
migrateTo :: DbVersion -> HS.Session ()
@@ -1159,7 +1218,7 @@ migrateFrom = \ver ->
11591218
ver@DbVersion1 -> do
11601219
HS.statement () $ Statement
11611220
(BS8.unwords
1162-
[ "DROP TABLE IF EXISTS account"
1221+
[ "DROP TABLE IF EXISTS account CASCADE"
11631222
]
11641223
) HE.unit HD.unit True
11651224
HS.statement () $ Statement
@@ -1215,8 +1274,7 @@ readDbVersion = do
12151274
, ");"
12161275
]
12171276
) HE.unit HD.unit True
1218-
migrateFrom minBound
1219-
pure $ Right $ Just maxBound
1277+
pure $ Right Nothing
12201278

12211279
latestDbVersion :: DbVersion
12221280
latestDbVersion = maxBound
@@ -1230,17 +1288,18 @@ dbVersionFromText :: T.Text -> Maybe DbVersion
12301288
dbVersionFromText t =
12311289
find (\ver -> dbVersionToText ver == t) [minBound .. maxBound]
12321290

1233-
-- XXX: this is not quite right, it'll never do the Version1 migration. Not a
1234-
-- problem currently since we dump everything at v2 anyway.
12351291
migrate :: HS.Session ()
12361292
migrate = do
12371293
readDbVersion >>= \case
12381294
Left e -> error $ show e
1239-
Right Nothing -> migrateFrom minBound
1295+
Right Nothing -> do
1296+
liftIO $ putStrLn "Migrating from beginning"
1297+
migrateFrom minBound
12401298
Right (Just v) ->
1241-
if v >= maxBound
1242-
then pure ()
1243-
else migrateFrom v
1299+
if
1300+
| v == maxBound -> pure ()
1301+
| v > maxBound -> error "V greater than maxbound"
1302+
| v < maxBound -> migrateFrom (succ v)
12441303

12451304
getDbInterface :: HC.Connection -> IO DbInterface
12461305
getDbInterface conn = do
@@ -1251,8 +1310,8 @@ getDbInterface conn = do
12511310
pure $ DbInterface
12521311
{ dbGetAllUsers = wrap usersGetSession
12531312
, dbGetUserById = \uid -> wrap (usersGetUserIdSession uid)
1254-
, dbCreateUser = \uid user -> wrap (usersPostSession uid user)
1255-
, dbUpdateUser = \uid user -> wrap (usersPutSession uid user)
1313+
, dbCreateUser = \uid uinfo -> wrap (usersPostSession uid uinfo)
1314+
, dbUpdateUser = \uid uinfo -> wrap (usersPutSession uid uinfo)
12561315
, dbDeleteUser = \uid -> Right <$> wrap (usersDeleteSession uid)
12571316
}
12581317
where

0 commit comments

Comments
 (0)