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

Commit 39f4311

Browse files
committed
Dirty implementation of presentation content
1 parent e5823c0 commit 39f4311

File tree

6 files changed

+166
-62
lines changed

6 files changed

+166
-62
lines changed

infra/handler/app/Test.hs

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ withEnv act = do
5050

5151
withServer :: (Warp.Port -> IO a) -> IO a
5252
withServer act =
53-
withEnv $ \env -> withS3 env $ withSQS env $ withDynamoDB env $
53+
withPresURL $ withEnv $ \env -> withS3 env $ withSQS env $ withDynamoDB env $
5454
withPristineDB $ \conn -> do
5555
(port, socket) <- Warp.openFreePort
5656
let warpSettings = Warp.setPort port $ Warp.defaultSettings
@@ -64,6 +64,9 @@ withServer act =
6464
) >>= \case
6565
Left () -> error "Server returned"
6666
Right a -> pure a
67+
where
68+
withPresURL =
69+
bracket_ (setEnv "DECKGO_PRESENTATIONS_URL" "foo.bar.baz") (unsetEnv "DECKGO_PRESENTATIONS_URL")
6770

6871
is'
6972
:: Aws.AsError a
@@ -204,10 +207,24 @@ main = do
204207

205208
testPresDeploys :: IO ()
206209
testPresDeploys = withEnv $ \env -> withS3 env $ do
207-
deployPresentation env (Username "josph") (Deckname "some-deck")
210+
let someFirebaseId = FirebaseId "the-uid" -- from ./token
211+
let someUserId = UserId someFirebaseId
212+
213+
let someSlide = Slide (Just "foo") "bar" HMS.empty
214+
someSlideId = SlideId "foo-id"
215+
216+
let newDeck = Deck
217+
{ deckSlides = [ someSlideId ]
218+
, deckDeckname = Deckname "bar"
219+
, deckDeckbackground = Just (Deckbackground "bar")
220+
, deckOwnerId = someUserId
221+
, deckAttributes = HMS.singleton "foo" "bar"
222+
}
223+
224+
deployPresentation env (Username "josph") newDeck [someSlide]
208225
-- XXX: tests the obj diffing by making sure we can upload a presentation
209226
-- twice without errors
210-
deployPresentation env (Username "josph") (Deckname "some-deck")
227+
deployPresentation env (Username "josph") newDeck [someSlide]
211228

212229
testUsersGet :: IO ()
213230
testUsersGet = withPristineDB $ \conn -> do
@@ -388,10 +405,30 @@ testServer = withServer $ \port -> do
388405
Right users -> error $ "Expected 0 users, got: " <> show users
389406

390407
runClientM (decksGet' b (Just someUserId)) clientEnv >>= \case
408+
-- TODO: shouldn't this be a 404?
391409
Left e -> error $ "Expected decks, got error: " <> show e
392410
Right [] -> pure ()
393411
Right decks -> error $ "Expected 0 decks, got: " <> show decks
394412

413+
414+
let someUserInfo = UserInfo
415+
{ userInfoFirebaseId = someFirebaseId
416+
, userInfoEmail = Just "[email protected]" }
417+
Right someUser = userInfoToUser someUserInfo
418+
419+
runClientM (usersPost' b someUserInfo) clientEnv >>= \case
420+
Left e -> error $ "Expected user, got error: " <> show e
421+
Right (Item userId user) ->
422+
if user == someUser && userId == someUserId then pure () else (error $ "Expected same user, got: " <> show user)
423+
424+
runClientM (usersPost' b someUserInfo) clientEnv >>= \case
425+
-- TODO: test that user is returned here, even on 409
426+
Left (FailureResponse resp) ->
427+
if HTTP.statusCode (responseStatusCode resp) == 409 then pure () else
428+
error $ "Got unexpected response: " <> show resp
429+
Left e -> error $ "Expected 409, got error: " <> show e
430+
Right item -> error $ "Expected failure, got success: " <> show item
431+
395432
deckId <- runClientM (decksPost' b someDeck) clientEnv >>= \case
396433
Left e -> error $ "Expected new deck, got error: " <> show e
397434
Right (Item deckId _) -> pure deckId
@@ -416,7 +453,7 @@ testServer = withServer $ \port -> do
416453

417454
runClientM (decksPostPublish' b deckId) clientEnv >>= \case
418455
Left e -> error $ "Expected publish, got error: " <> show e
419-
Right () -> pure ()
456+
Right {} -> pure ()
420457

421458
runClientM (decksGet' b (Just someUserId)) clientEnv >>= \case
422459
Left e -> error $ "Expected decks, got error: " <> show e
@@ -456,24 +493,6 @@ testServer = withServer $ \port -> do
456493
Right decks ->
457494
unless (decks == []) (error $ "Expected no decks, got: " <> show decks)
458495

459-
let someUserInfo = UserInfo
460-
{ userInfoFirebaseId = someFirebaseId
461-
, userInfoEmail = Just "[email protected]" }
462-
Right someUser = userInfoToUser someUserInfo
463-
464-
runClientM (usersPost' b someUserInfo) clientEnv >>= \case
465-
Left e -> error $ "Expected user, got error: " <> show e
466-
Right (Item userId user) ->
467-
if user == someUser && userId == someUserId then pure () else (error $ "Expected same user, got: " <> show user)
468-
469-
runClientM (usersPost' b someUserInfo) clientEnv >>= \case
470-
-- TODO: test that user is returned here, even on 409
471-
Left (FailureResponse resp) ->
472-
if HTTP.statusCode (responseStatusCode resp) == 409 then pure () else
473-
error $ "Got unexpected response: " <> show resp
474-
Left e -> error $ "Expected 409, got error: " <> show e
475-
Right item -> error $ "Expected failure, got success: " <> show item
476-
477496
-- TODO: test that creating user with token that has different user as sub
478497
-- fails
479498

@@ -485,7 +504,7 @@ _usersDelete' :: T.Text -> UserId -> ClientM ()
485504

486505
decksGet' :: T.Text -> Maybe UserId -> ClientM [Item DeckId Deck]
487506
decksGetDeckId' :: T.Text -> DeckId -> ClientM (Item DeckId Deck)
488-
decksPostPublish' :: T.Text -> DeckId -> ClientM ()
507+
decksPostPublish' :: T.Text -> DeckId -> ClientM PresResponse
489508
decksPost' :: T.Text -> Deck -> ClientM (Item DeckId Deck)
490509
decksPut' :: T.Text -> DeckId -> Deck -> ClientM (Item DeckId Deck)
491510
decksDelete' :: T.Text -> DeckId -> ClientM ()

infra/handler/package.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies:
2929
- servant-swagger
3030
- servant-swagger-ui
3131
- swagger2
32+
- tagsoup
3233
- text
3334
- unliftio
3435
- temporary

infra/handler/src/DeckGo/Handler.hs

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ instance Aeson.ToJSON UserInfo where
201201
instance ToSchema (Item UserId User) where
202202
declareNamedSchema _ = pure $ NamedSchema (Just "UserWithId") mempty
203203

204+
instance ToSchema PresResponse where
205+
declareNamedSchema _ = pure $ NamedSchema (Just "PresResponse") mempty
206+
204207
instance ToSchema User where
205208
declareNamedSchema _ = pure $ NamedSchema (Just "User") mempty
206209

@@ -226,7 +229,7 @@ type DecksAPI =
226229
Protected :>
227230
Capture "deck_id" DeckId :>
228231
"publish" :>
229-
Post '[JSON] () :<|>
232+
Post '[JSON] PresResponse :<|> -- XXX
230233
Protected :> ReqBody '[JSON] Deck :> Post '[JSON] (Item DeckId Deck) :<|>
231234
Protected :>
232235
Capture "deck_id" DeckId :>
@@ -385,7 +388,7 @@ server env conn = serveUsers :<|> serveDecks :<|> serveSlides
385388
serveDecks =
386389
decksGet env :<|>
387390
decksGetDeckId env :<|>
388-
decksPostPublish env :<|>
391+
decksPostPublish env conn :<|>
389392
decksPost env :<|>
390393
decksPut env :<|>
391394
decksDelete env
@@ -760,8 +763,24 @@ decksGetDeckId env fuid deckId = do
760763

761764
pure deck
762765

763-
decksPostPublish :: Aws.Env -> Firebase.UserId -> DeckId -> Servant.Handler ()
764-
decksPostPublish (fixupEnv -> env) _ deckId = do
766+
data PresResponse = PresResponse T.Text
767+
768+
instance Aeson.ToJSON PresResponse where
769+
toJSON (PresResponse t) = Aeson.object [ "url" .= t ]
770+
771+
instance Aeson.FromJSON PresResponse where
772+
parseJSON = Aeson.withObject "pres-response" $ \o ->
773+
PresResponse <$> o .: "url"
774+
775+
776+
decksPostPublish
777+
:: Aws.Env
778+
-> HC.Connection
779+
-> Firebase.UserId
780+
-> DeckId
781+
-> Servant.Handler PresResponse
782+
-- TODO: AUTH!!!!
783+
decksPostPublish (fixupEnv -> env) conn _ deckId = do
765784

766785
-- TODO: check auth
767786

@@ -788,6 +807,23 @@ decksPostPublish (fixupEnv -> env) _ deckId = do
788807
liftIO $ print e
789808
Servant.throwError Servant.err500
790809

810+
presUrl <- liftIO (getEnv "DECKGO_PRESENTATIONS_URL")
811+
liftIO (deckGetDeckIdDB env deckId) >>= \case
812+
Nothing -> Servant.throwError Servant.err500
813+
Just deck -> do
814+
let dname = deckDeckname deck
815+
iface <- liftIO $ getDbInterface conn
816+
liftIO (fmap itemContent <$> dbGetUserById iface (deckOwnerId deck)) >>= \case
817+
Nothing -> do
818+
liftIO $ putStrLn "No User Id"
819+
Servant.throwError Servant.err500
820+
Just user -> case userUsername user of
821+
Nothing -> do
822+
liftIO $ putStrLn "No username"
823+
Servant.throwError Servant.err500
824+
Just uname ->
825+
pure $ PresResponse $ "https://" <> T.pack presUrl <> "/" <> presentationPrefix uname dname
826+
791827
fixupEnv :: Aws.Env -> Aws.Env
792828
fixupEnv = Aws.configure $ SQS.sqs
793829
{ Aws._svcEndpoint = \reg -> do
@@ -960,7 +996,13 @@ slidesPutStatement = Statement sql encoder decoder True
960996
contramap (Aeson.toJSON . slideAttributes . view _2) (HE.param HE.json)
961997
decoder = HD.unit
962998

963-
slidesGetSlideId :: Aws.Env -> HC.Connection -> Firebase.UserId -> DeckId -> SlideId -> Servant.Handler (Item SlideId Slide)
999+
slidesGetSlideId
1000+
:: Aws.Env
1001+
-> HC.Connection
1002+
-> Firebase.UserId
1003+
-> DeckId
1004+
-> SlideId
1005+
-> Servant.Handler (Item SlideId Slide)
9641006
slidesGetSlideId env conn fuid deckId slideId = do
9651007

9661008
getDeck env deckId >>= \case
@@ -1452,3 +1494,20 @@ dynamoSet exprs = T.unwords exprs'
14521494
(sts, removes) = foldr f ([], []) exprs
14531495
f (Set l r) (ls, rs) = (ls <> [l <> " = " <> r], rs)
14541496
f (Remove t ) (ls, rs) = (ls, rs <> [t])
1497+
1498+
presentationPrefix :: Username -> Deckname -> T.Text
1499+
presentationPrefix uname dname =
1500+
unUsername uname <> "/" <> sanitizeDeckname dname <> "/"
1501+
1502+
sanitizeDeckname :: Deckname -> T.Text
1503+
sanitizeDeckname = T.toLower . strip . dropBadChars . unDeckname
1504+
where
1505+
strip :: T.Text -> T.Text
1506+
strip = T.dropAround ( == '-' )
1507+
dropBadChars :: T.Text -> T.Text
1508+
dropBadChars = T.concatMap
1509+
$ \case
1510+
c | isAscii c && isAlphaNum c -> T.singleton c
1511+
| c == ' ' -> T.singleton '-'
1512+
| otherwise -> ""
1513+

infra/handler/src/DeckGo/Presenter.hs

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ module DeckGo.Presenter where
1111
import Control.Lens hiding ((.=))
1212
import Control.Monad
1313
import Data.Bifunctor
14-
import Data.Char (isAscii, isAlphaNum)
1514
import Data.Function
1615
import Data.List (foldl')
16+
import Data.Maybe
1717
import Data.String
1818
import DeckGo.Handler
1919
import DeckGo.Prelude
@@ -36,6 +36,7 @@ import qualified Network.AWS.S3 as S3
3636
import qualified Network.Mime as Mime
3737
import qualified System.Directory as Dir
3838
import qualified System.IO.Temp as Temp
39+
import qualified Text.HTML.TagSoup as TagSoup
3940

4041
data Err = Err T.Text SomeException
4142
deriving (Show, Exception)
@@ -73,30 +74,67 @@ listPresentationObjects env bucket uname dname =
7374

7475
withPresentationFiles
7576
:: Username
76-
-> Deckname
77+
-> Deck
78+
-> [Slide]
7779
-> ([(FilePath, S3.ObjectKey, S3.ETag)] -> IO a)
7880
-> IO a
79-
withPresentationFiles uname dname act = do
81+
withPresentationFiles uname deck slides act = do
8082
deckgoStarterDist <- getEnv "DECKGO_STARTER_DIST"
8183
Temp.withSystemTempDirectory "dist" $ \dir -> do
8284
Tar.extract dir deckgoStarterDist
83-
interpolateFile uname dname $ dir </> "index.html"
84-
interpolateFile uname dname $ dir </> "manifest.json"
85+
mapFile processIndex $ dir </> "index.html"
86+
mapFile interpol $ dir </> "manifest.json"
8587
putStrLn "Listing files..."
8688
files <- listDirectoryRecursive dir
8789
files' <- forM files $ \(fp, components) -> do
8890
etag <- fileETag fp
8991
let okey = mkObjectKey uname dname components
9092
pure (fp, okey, etag)
9193
act files'
92-
93-
interpolateFile :: Username -> Deckname -> FilePath -> IO ()
94-
interpolateFile uname dname fp = do
95-
T.readFile fp >>= T.writeFile fp . interpol
9694
where
95+
dname = deckDeckname deck
96+
processIndex :: T.Text -> T.Text
97+
processIndex =
98+
TagSoup.renderTags . processTags deck slides . TagSoup.parseTags .
99+
interpol
97100
interpol =
98101
T.replace "{{DECKDECKGO_TITLE}}" (unDeckname dname) .
99-
T.replace "{{DECKDECKGO_AUTHOR}}" (unUsername uname)
102+
T.replace "{{DECKDECKGO_AUTHOR}}" (unUsername uname) .
103+
-- TODO: description
104+
T.replace "{{DECKDECKGO_DESCRIPTION}}" "(no description given)" .
105+
T.replace "{{DECKDECKGO_BASE_HREF}}"
106+
("/" <> presentationPrefix uname dname)
107+
108+
mapFile :: (T.Text -> T.Text) -> FilePath -> IO ()
109+
mapFile f fp = do
110+
T.readFile fp >>= T.writeFile fp . f
111+
112+
type Tag = TagSoup.Tag T.Text
113+
114+
processTags :: Deck -> [Slide] -> [Tag] -> [Tag]
115+
processTags deck slides = concatMap $ \case
116+
TagSoup.TagOpen str (HMS.fromList -> attrs)
117+
| str == "deckgo-deck" -> do
118+
[ TagSoup.TagOpen str (HMS.toList (deckAttributes deck <> attrs)) ] <>
119+
(concatMap slideTags slides) <>
120+
(maybe [] (\dbg ->
121+
[deckBackgroundTag dbg])
122+
(deckDeckbackground deck))
123+
t -> [t]
124+
125+
deckBackgroundTag :: Deckbackground -> Tag
126+
deckBackgroundTag (unDeckbackground -> bg) = TagSoup.TagText bg
127+
128+
slideTags :: Slide -> [Tag]
129+
slideTags slide =
130+
[ TagSoup.TagOpen
131+
("deckgo-slide-" <> slideTemplate slide)
132+
(HMS.toList (slideAttributes slide))
133+
] <> maybe [] TagSoup.parseTags (slideContent slide) <>
134+
[ TagSoup.TagClose
135+
("deckgo-slide-" <> slideTemplate slide)
136+
]
137+
100138

101139
listObjects :: Aws.Env -> S3.BucketName -> Maybe T.Text -> IO [S3.Object]
102140
listObjects (fixupEnv' -> env) bname mpref = xif ([],Nothing) $ \f (es, ct) ->
@@ -136,17 +174,19 @@ deployDeck env conn deckId = do
136174
Nothing -> pure () -- TODO
137175
Just user -> case userUsername user of
138176
Nothing -> pure () -- TODO
139-
Just uname ->
140-
deployPresentation env uname (deckDeckname deck)
177+
Just uname -> do
178+
slides <- catMaybes <$> mapM (dbGetSlideById iface) (deckSlides deck)
179+
deployPresentation env uname deck slides
141180

142-
deployPresentation :: Aws.Env -> Username -> Deckname -> IO ()
143-
deployPresentation (fixupEnv' -> env) uname dname = do
181+
deployPresentation :: Aws.Env -> Username -> Deck -> [Slide] -> IO ()
182+
deployPresentation (fixupEnv' -> env) uname deck slides = do
144183
bucketName <- getEnv "BUCKET_NAME"
145184
let bucket = S3.BucketName (T.pack bucketName)
185+
let dname = deckDeckname deck
146186
putStrLn "Listing current objects"
147187
currentObjs <- listPresentationObjects env bucket uname dname
148188
putStrLn "Listing presentations files"
149-
withPresentationFiles uname dname $ \files -> do
189+
withPresentationFiles uname deck slides $ \files -> do
150190
let
151191
currentObjs' =
152192
(\obj ->
@@ -207,26 +247,10 @@ fixupEnv' = Aws.configure $ S3.s3
207247
(Aws._svcEndpoint S3.s3 reg) & Aws.endpointHost .~ T.encodeUtf8 new
208248
}
209249

210-
presentationPrefix :: Username -> Deckname -> T.Text
211-
presentationPrefix uname dname =
212-
unUsername uname <> "/" <> sanitizeDeckname dname <> "/"
213-
214250
mkObjectKey :: Username -> Deckname -> [T.Text] -> S3.ObjectKey
215251
mkObjectKey uname dname components = S3.ObjectKey $
216252
presentationPrefix uname dname <> T.intercalate "/" components
217253

218-
sanitizeDeckname :: Deckname -> T.Text
219-
sanitizeDeckname = T.toLower . strip . dropBadChars . unDeckname
220-
where
221-
strip :: T.Text -> T.Text
222-
strip = T.dropAround ( == '-' )
223-
dropBadChars :: T.Text -> T.Text
224-
dropBadChars = T.concatMap
225-
$ \case
226-
c | isAscii c && isAlphaNum c -> T.singleton c
227-
| c == ' ' -> T.singleton '-'
228-
| otherwise -> ""
229-
230254
fileETag :: FilePath -> IO S3.ETag
231255
fileETag fp =
232256
-- XXX: The 'show' step is very import, it's what converts the Digest to

infra/lambda.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ resource "aws_lambda_function" "api" {
2121
QUEUE_NAME = "${aws_sqs_queue.presentation_deploy.name}"
2222
GOOGLE_PUBLIC_KEYS = "google-public-keys.json"
2323
FIREBASE_PROJECT_ID = "deckdeckgo-studio-beta"
24+
DECKGO_PRESENTATIONS_URL = "${aws_route53_record.www_site.fqdn}"
2425
}
2526
}
2627
}

0 commit comments

Comments
 (0)