From e50e234747498b231ba4642d97d0bba01d93d819 Mon Sep 17 00:00:00 2001 From: Mats Rauhala Date: Tue, 14 Aug 2018 22:11:52 +0300 Subject: [PATCH] Start working opds (#2) - Relates to #2 --- ebook-manager.cabal | 3 --- src/API/Catalogue.hs | 45 +++++++++++++++++++++++++------------------- src/Servant/XML.hs | 10 ++++++++++ 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/ebook-manager.cabal b/ebook-manager.cabal index 2d699d6..4d262ee 100644 --- a/ebook-manager.cabal +++ b/ebook-manager.cabal @@ -1,6 +1,3 @@ --- Initial ebook-manager.cabal generated by cabal init. For further --- documentation, see http://haskell.org/cabal/users-guide/ - name: ebook-manager version: 0.1.0.0 -- synopsis: diff --git a/src/API/Catalogue.hs b/src/API/Catalogue.hs index de125dc..37a813c 100644 --- a/src/API/Catalogue.hs +++ b/src/API/Catalogue.hs @@ -98,28 +98,33 @@ class Monad m => VersionedCatalog m (v :: Nat) where getChannels :: SafeUser -> m (Catalog v) instance VersionedCatalog AppM 1 where - getChannels SafeUser{username} = do - updated <- liftIO getCurrentTime - let self = Rel ("/api/current/" <> selfUrl) - -- I'm not sure if this safe link approach is really useable with this - -- api hierarchy since I can't access the topmost api from here. Also - -- authentication would bring a little bit of extra effort as well - selfUrl = pack . uriPath . linkURI $ safeLink (Proxy @(BaseAPI 1)) (Proxy @(RootCatalog 1)) - start = self - pagination = Pagination Nothing Nothing - entries <- map (fromChannel updated) <$> runDB (Channel.userChannels username) - pure CatalogV1{..} - where - fromChannel :: UTCTime -> Channel.Channel -> Entry 1 - fromChannel updated Channel.Channel{..} = - let url = pack . uriPath . linkURI $ safeLink (Proxy @(BaseAPI 1)) (Proxy @(ChannelCatalog 1)) identifier - self = Rel ("/api/current/" <> url) - in EntryV1 channel channel updated channel (Left $ SubSection self) + getChannels = getChannelsV1 + +getChannelsV1 :: SafeUser -> AppM (Catalog 1) +getChannelsV1 SafeUser{username} = do + updated <- liftIO getCurrentTime + let self = Rel ("/api/current/" <> selfUrl) + -- I'm not sure if this safe link approach is really useable with this + -- api hierarchy since I can't access the topmost api from here. Also + -- authentication would bring a little bit of extra effort as well + selfUrl = pack . uriPath . linkURI $ safeLink (Proxy @(BaseAPI 1)) (Proxy @(RootCatalog 1)) + start = self + pagination = Pagination Nothing Nothing + entries <- map (fromChannel updated) <$> runDB (Channel.userChannels username) + pure CatalogV1{..} + where + fromChannel :: UTCTime -> Channel.Channel -> Entry 1 + fromChannel updated Channel.Channel{..} = + let url = pack . uriPath . linkURI $ safeLink (Proxy @(BaseAPI 1)) (Proxy @(ChannelCatalog 1)) identifier + self = Rel ("/api/current/" <> url) + in EntryV1 channel channel updated channel (Left $ SubSection self) type VersionedAPI (v :: Nat) = Auth '[SA.BasicAuth, SA.JWT] SafeUser :> BaseAPI v -type RootCatalog (v :: Nat) = "catalog" :> Get '[XML] (Catalog v) -type ChannelCatalog (v :: Nat) = "catalog" :> "channel" :> Capture "channel_id" Channel.ChannelID :> Get '[XML] (Catalog v) +type CatalogContent = '[XML, OPDS] + +type RootCatalog (v :: Nat) = "catalog" :> Get CatalogContent (Catalog v) +type ChannelCatalog (v :: Nat) = "catalog" :> "channel" :> Capture "channel_id" Channel.ChannelID :> Get CatalogContent (Catalog v) type BaseAPI (v :: Nat) = RootCatalog v :<|> ChannelCatalog v @@ -127,6 +132,8 @@ handler :: forall v. VersionedCatalog AppM v => ServerT (VersionedAPI v) AppM handler auth = catalogRoot :<|> catalogChannels where catalogChannels :: Channel.ChannelID -> AppM (Catalog v) + -- Channel specific catalog returns tags inside the catalog catalogChannels _ = throwM err403{errBody="Not implemented"} catalogRoot :: AppM (Catalog v) + -- catalog root returns channels catalogRoot = flip requireLoggedIn auth getChannels diff --git a/src/Servant/XML.hs b/src/Servant/XML.hs index 42a2fca..cbc2c7b 100644 --- a/src/Servant/XML.hs +++ b/src/Servant/XML.hs @@ -1,9 +1,11 @@ {-# Language OverloadedStrings #-} {-# Language FlexibleInstances #-} {-# Language MultiParamTypeClasses #-} +{-# Language TypeApplications #-} module Servant.XML ( ToNode(..) , XML + , OPDS , Text.Hamlet.XML.xml , iso8601 ) where @@ -16,14 +18,22 @@ import Network.HTTP.Media.MediaType data XML +data OPDS + instance (ToNode a) => MimeRender XML a where mimeRender _ a = let [NodeElement root] = toNode a in renderLBS def (Document (Prologue [] Nothing []) root []) +instance (ToNode a) => MimeRender OPDS a where + mimeRender _ a = mimeRender (Proxy @XML) a + instance Accept XML where contentType _ = "application" // "xml" /: ("charset", "utf-8") +instance Accept OPDS where + contentType _ = "application" // "atom+xml" /: ("charset", "utf-8") /: ("profile", "opds-catalog") + iso8601 :: UTCTime -> Text iso8601 = pack . formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%SZ"