Compare commits

...
4 Commits
Author SHA1 Message Date
paul 79cebeb52a Add CI config
continuous-integration/drone/push Build is passing
2022-09-21 23:50:13 +02:00
paul 98ef505453 Add orders route 2022-09-21 23:40:06 +02:00
paul 6763d3ffa1 Add todo 2022-09-16 22:21:31 +02:00
paul 8901ec0eb4 Add rudimentary error handler thingy 2022-09-16 22:20:43 +02:00
5 changed files with 63 additions and 7 deletions
+9
View File
@@ -0,0 +1,9 @@
---
kind: pipeline
type: docker
name: default
steps:
- name: stack build
image: haskell:9.2.4-slim
commands:
- stack build
+1
View File
@@ -8,3 +8,4 @@ Webserver that offers an HTML-only interface to Squeak.
- Improve error handling: Write functions that turn `Maybe` and `Either` `SpockAction`s that return `4xx` or `5xx`. - Improve error handling: Write functions that turn `Maybe` and `Either` `SpockAction`s that return `4xx` or `5xx`.
- Document JSON stuff - Document JSON stuff
- Record times: How long did HTTP requests take?
+20 -4
View File
@@ -9,7 +9,7 @@ module Lisa
import Control.Monad.IO.Class (MonadIO) import Control.Monad.IO.Class (MonadIO)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Text (Text) import Data.Text (Text)
import Network.HTTP.Types.Status (badRequest400) import Network.HTTP.Types.Status (Status, badRequest400, internalServerError500)
import Text.Blaze.Html (Html) import Text.Blaze.Html (Html)
import Text.Blaze.Html.Renderer.Utf8 (renderHtml) import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
import Web.Internal.HttpApiData (FromHttpApiData) import Web.Internal.HttpApiData (FromHttpApiData)
@@ -38,6 +38,18 @@ requiredParam k = param k >>= \case
Just val -> Just val ->
pure val pure val
-- TODO: Generalize, introduce some sort of Lisa.Types.Error type
-- TODO: Make more readable
onLeft :: MonadIO m => (ActionCtxT ctx m (Either e a)) -> (Status, Text) -> ActionCtxT ctx m a
onLeft f (status, message) = f >>= (flip either pure $ \_ -> do
setStatus status
text message)
onLeftBlaze :: MonadIO m => (ActionCtxT ctx m (Either e a)) -> (Status, e -> Html) -> ActionCtxT ctx m a
onLeftBlaze f (status, view) = f >>= (flip either pure $ \err -> do
setStatus status
blaze $ view err)
-- Exports -- Exports
mkConfig :: IO (SpockCfg () Session State) mkConfig :: IO (SpockCfg () Session State)
@@ -51,15 +63,19 @@ app = do
blaze Views.viewIndex blaze Views.viewIndex
get ("lectures" <//> var) $ \lid -> do get ("lectures" <//> var) $ \lid -> do
lecture <- getLectureByIdCached lid lecture <- getLectureByIdCached lid `onLeft` (internalServerError500, "Failed to retrieve lectures")
documents <- Squeak.getDocumentsByLectureId lid documents <- Squeak.getDocumentsByLectureId lid `onLeft` (internalServerError500, "Failed to retrieve documents")
blaze $ either Views.viewSqueakError id $ Views.viewLecture <$> lecture <*> documents blaze $ Views.viewLecture lecture documents
get "lectures" $ do get "lectures" $ do
query <- fromMaybe "" <$> param "query" query <- fromMaybe "" <$> param "query"
lectures <- getAllLecturesCached lectures <- getAllLecturesCached
blaze $ either Views.viewSqueakError (Views.viewLectures query) lectures blaze $ either Views.viewSqueakError (Views.viewLectures query) lectures
get "orders" $ do
orders <- Squeak.getAllPendingOrders `onLeftBlaze` (internalServerError500, Views.viewSqueakError)
blaze $ Views.viewOrders orders
get "login" $ blaze Views.viewLogin get "login" $ blaze Views.viewLogin
post "login" $ do post "login" $ do
+19 -1
View File
@@ -11,6 +11,7 @@ import Data.Aeson (FromJSON(parseJSON), Options(fieldLabelModifier), ToJSON(toJS
import Data.Text (Text) import Data.Text (Text)
import Data.Text.Encoding (encodeUtf8) import Data.Text.Encoding (encodeUtf8)
import Data.Time.Calendar (Day) import Data.Time.Calendar (Day)
import Data.Time.LocalTime (ZonedTime)
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Network.HTTP.Req (Option, Scheme(..), POST(..), ReqBodyJson(..), Url, defaultHttpConfig, header, http, port, jsonResponse, responseBody, req, runReq) import Network.HTTP.Req (Option, Scheme(..), POST(..), ReqBodyJson(..), Url, defaultHttpConfig, header, http, port, jsonResponse, responseBody, req, runReq)
@@ -102,6 +103,13 @@ instance FromJSON Documents where
parseJSON = withObject "Documents" $ \v -> Documents parseJSON = withObject "Documents" $ \v -> Documents
<$> ((v .: "documents") >>= (.: "results")) <$> ((v .: "documents") >>= (.: "results"))
data Orders = Orders { unOrders :: [Order] }
deriving (Show)
instance FromJSON Orders where
parseJSON = withObject "Orders" $ \v -> Orders
<$> ((v .: "orders") >>= (.: "results"))
-- | Helper for automatically writing 'FromJSON' instances. -- | Helper for automatically writing 'FromJSON' instances.
-- --
-- >>> removePrefixModifier "document" "documentId" -- >>> removePrefixModifier "document" "documentId"
@@ -188,6 +196,14 @@ getDocumentsByLectureId lecture = fmap unDocuments <$> joinErrors <$> mkQueryWit
\ } \ \ } \
\}" \}"
getAllPendingOrders :: (SqueakCtx m, MonadIO m) => m (Either SqueakError [Order])
getAllPendingOrders = fmap unOrders <$> joinErrors <$> mkQuery
"{ orders(filters: {state: PENDING}) \
\ { results \
\ { id created numPages price tag } \
\ } \
\}"
-- Mutations -- Mutations
data LoginResult = LoginResult { unLoginResult :: SqueakErrorOr Credentials } data LoginResult = LoginResult { unLoginResult :: SqueakErrorOr Credentials }
@@ -255,8 +271,10 @@ instance (FromJSON CreateOrderResult) where
data Order = Order data Order = Order
{ orderId :: Text { orderId :: Text
, orderTag :: Text , orderCreated :: ZonedTime
, orderNumPages :: Int , orderNumPages :: Int
, orderPrice :: Double
, orderTag :: Text
} }
deriving (Generic, Show) deriving (Generic, Show)
+13 -1
View File
@@ -12,7 +12,7 @@ import Text.Blaze.Html5 hiding (map, style)
import Text.Blaze.Html5.Attributes hiding (form, label) import Text.Blaze.Html5.Attributes hiding (form, label)
import Lisa.Types (Session) import Lisa.Types (Session)
import Lisa.Squeak (Document(..), Lecture(..), SqueakError(..)) import Lisa.Squeak (Document(..), Lecture(..), Order(..), SqueakError(..))
viewIndex :: Html viewIndex :: Html
viewIndex = do viewIndex = do
@@ -20,6 +20,7 @@ viewIndex = do
ul $ do ul $ do
li $ a "Login" ! href "/login" li $ a "Login" ! href "/login"
li $ a "Vorlesungen" ! href "/lectures" li $ a "Vorlesungen" ! href "/lectures"
li $ a "Bestellungen" ! href "/orders"
li $ a "Debuginformation" ! href "/debug" li $ a "Debuginformation" ! href "/debug"
viewLectures :: Text -> [Lecture] -> Html viewLectures :: Text -> [Lecture] -> Html
@@ -49,6 +50,17 @@ viewLecture lecture documents = do
td $ text $ documentSemester document td $ text $ documentSemester document
td $ string $ show $ documentNumPages document td $ string $ show $ documentNumPages document
viewOrders :: [Order] -> Html
viewOrders orders = do
h1 $ text "Bestellungen"
table $ do
thead $ mapM_ th ["Bezeichner", "Seiten", "Preis", "Erstellt"]
tbody $ forM_ orders $ \order -> tr $ do
td $ text $ orderTag order
td $ string $ show $ orderNumPages order
td $ string $ show $ orderPrice order
td $ string $ show $ orderCreated order
viewLogin :: Html viewLogin :: Html
viewLogin = do viewLogin = do
h1 $ text "FSMI-Login" h1 $ text "FSMI-Login"