Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove waargonaut and revert to using aeson. #98

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion applied-fp-course.cabal
Expand Up @@ -86,9 +86,10 @@ library
, text ^>=1.2
, time >=1.4 && <1.10
, transformers >=0.4 && <0.6
, waargonaut >=0.6 && <0.9
, wai >=3.2 && <3.4
, warp >=3.2 && <3.4
, aeson >=1.5 && <3
, scientific >=0.3 && <0.4

-- Directories containing source files.
hs-source-dirs: src
Expand Down
4 changes: 0 additions & 4 deletions flake.nix
Expand Up @@ -20,12 +20,8 @@
root = ./.;

overrides = final: prev: with nixpkgs.haskell.lib; {
hw-json-simd = dontCheck (unmarkBroken prev.hw-json-simd);
hw-json-standard-cursor = doJailbreak prev.hw-json-standard-cursor;
natural = dontCheck (unmarkBroken prev.natural);
sqlite-simple-errors =
unmarkBroken (doJailbreak prev.sqlite-simple-errors);
waargonaut = dontCheck (doJailbreak prev.waargonaut);
};
};
in
Expand Down
16 changes: 6 additions & 10 deletions src/Level04/Core.hs
Expand Up @@ -14,12 +14,12 @@ import Network.Wai (Application, Request,
strictRequestBody)
import Network.Wai.Handler.Warp (run)

import Data.Aeson (ToJSON, encode)
import qualified Data.ByteString.Lazy.Char8 as LBS
import Network.HTTP.Types (Status, hContentType,
status200, status400,
status404, status500)

import qualified Data.ByteString.Lazy.Char8 as LBS

import Data.Either (Either (Left, Right),
either)

Expand All @@ -30,9 +30,6 @@ import Data.Text.Lazy.Encoding (encodeUtf8)

import Database.SQLite.SimpleErrors.Types (SQLiteResponse)

import Waargonaut.Encode (Encoder')
import qualified Waargonaut.Encode as E

import Level04.Conf (Conf, firstAppConfig)
import qualified Level04.DB as DB
import Level04.Types (ContentType (JSON, PlainText),
Expand Down Expand Up @@ -102,12 +99,11 @@ resp500 =
mkResponse status500

resp200Json
:: Encoder' a
-> a
:: ToJSON a
=> a
-> Response
resp200Json e =
mkResponse status200 JSON . encodeUtf8 .
E.simplePureEncodeTextNoSpaces e
resp200Json =
mkResponse status200 JSON . encode

-- |
app
Expand Down
23 changes: 6 additions & 17 deletions src/Level04/Types.hs
Expand Up @@ -16,6 +16,8 @@ module Level04.Types
, fromDBComment
) where

import Data.Aeson (ToJSON (..))

import GHC.Generics (Generic)

import Data.ByteString (ByteString)
Expand All @@ -24,14 +26,9 @@ import Data.Text (Text, pack)
import Data.List (stripPrefix)
import Data.Maybe (fromMaybe)

import Data.Functor.Contravariant ((>$<))

import Data.Time (UTCTime)
import qualified Data.Time.Format as TF

import Waargonaut.Encode (Encoder)
import qualified Waargonaut.Encode as E

import Level04.DB.Types (DBComment)

-- | Notice how we've moved these types into their own modules. It's cheap and
Expand Down Expand Up @@ -60,15 +57,13 @@ data Comment = Comment
deriving Show

-- | We're going to write the JSON encoder for our `Comment` type. We'll need to
-- consult the documentation in the 'Waargonaut.Encode' module to find the
-- consult the documentation in the 'Aeson' package to find the
-- relevant functions and instructions on how to use them:
--
-- 'https://hackage.haskell.org/package/waargonaut/docs/Waargonaut-Encode.html'
-- 'https://hackage.haskell.org/package/aeson/docs/Data-Aeson.html'
--
encodeComment :: Applicative f => Encoder f Comment
encodeComment =
error "Comment JSON encoder not implemented"
-- Tip: Use the 'encodeISO8601DateTime' to handle the UTCTime for us.
instance ToJSON Comment where
toJSON = error "Comment ToJSON instance no implemented"

-- | For safety we take our stored `DBComment` and try to construct a `Comment`
-- that we would be okay with showing someone. However unlikely it may be, this
Expand All @@ -95,10 +90,4 @@ renderContentType
renderContentType PlainText = "text/plain"
renderContentType JSON = "application/json"

encodeISO8601DateTime :: Applicative f => Encoder f UTCTime
encodeISO8601DateTime = pack . TF.formatTime loc fmt >$< E.text
where
fmt = TF.iso8601DateFormat (Just "%H:%M:%S")
loc = TF.defaultTimeLocale { TF.knownTimeZones = [] }

-- | Move on to ``src/Level04/DB.hs`` next.
9 changes: 3 additions & 6 deletions src/Level04/Types/CommentText.hs
Expand Up @@ -4,13 +4,11 @@ module Level04.Types.CommentText
, getCommentText
) where

import Waargonaut.Encode (Encoder)
import qualified Waargonaut.Encode as E
import Data.Aeson (ToJSON (..))

import Level04.Types.Error (Error (EmptyCommentText),
nonEmptyText)

import Data.Functor.Contravariant (contramap)
import Data.Text (Text)

newtype CommentText = CommentText Text
Expand Down Expand Up @@ -52,6 +50,5 @@ getCommentText (CommentText t) =
-- functions. There is a quick introduction to `Contravariant` in the `README`
-- for this level.
--
encodeCommentText :: Applicative f => Encoder f CommentText
encodeCommentText = -- Try using 'contramap' and 'E.text'.
error "CommentText JSON encoder not implemented"
instance ToJSON CommentText where
toJSON = error "CommentText JSON encoder not implemented"
42 changes: 7 additions & 35 deletions src/Level04/Types/Topic.hs
Expand Up @@ -2,11 +2,9 @@ module Level04.Types.Topic
( Topic
, mkTopic
, getTopic
, encodeTopic
) where

import Waargonaut.Encode (Encoder)
import qualified Waargonaut.Encode as E
import Data.Aeson (ToJSON (..))

import Data.Functor.Contravariant (contramap)
import Data.Text (Text)
Expand All @@ -28,37 +26,11 @@ getTopic
getTopic (Topic t) =
t

-- | We will use this function to describe how we would like our `Topic`
-- type to be encoded into JSON.
-- | We're going to write the JSON encoder for our `Topic` type. We'll need to consult the
-- documentation in the 'Aeson' package to find the relevant functions and instructions on how to
-- use them:
--
-- Waargonaut knows how to encode a `Text` value, we need a way of telling it
-- how to unwrap our newtype to encode the `Text` value inside.
-- 'https://hackage.haskell.org/package/aeson/docs/Data-Aeson.html'
--
-- We _could_ write the code to unpack or pattern match on the `Topic` and
-- then run the `Text` encoder using that value as input before returning that
-- as the result of our Encoder. Something like this:
--
-- @
-- encodeA $ \(Topic t) -> runEncoder text t
-- @
--
-- But like many of the tasks that we've been completing in this course, the
-- plumbing for such a thing has already been written for us. Sometimes the
-- instances of the structure we're trying to create may provide a handy
-- shortcut.
--
-- In this case the `Encoder` type has an instance of `Contravariant`. Which has
-- the following function:
--
-- @
-- contramap :: Contravariant f => (a -> b) -> f b -> f a
-- @
--
-- In this case the `Encoder` type has an instance of `Contravariant`. That
-- typeclass has a function that comes in very handy when writing these
-- functions. There is a quick introduction to `Contravariant` in the `README`
-- for this level.
--
encodeTopic :: Applicative f => Encoder f Topic
encodeTopic = -- Try using 'contramap' and 'E.text'
error "topic JSON encoder not implemented"
instance ToJSON Topic where
toJSON = error "Topic ToJSON instance no implemented"
17 changes: 7 additions & 10 deletions src/Level05/Core.hs
Expand Up @@ -28,8 +28,7 @@ import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import Data.Text.Lazy.Encoding (encodeUtf8)

import Waargonaut.Encode (Encoder')
import qualified Waargonaut.Encode as E
import Data.Aeson (ToJSON, encode)

import Database.SQLite.SimpleErrors.Types (SQLiteResponse)

Expand All @@ -39,7 +38,6 @@ import qualified Level05.DB as DB
import Level05.Types (ContentType (..),
Error (..),
RqType (AddRq, ListRq, ViewRq),
encodeComment, encodeTopic,
mkCommentText, mkTopic,
renderContentType)

Expand Down Expand Up @@ -116,12 +114,11 @@ resp500 =
mkResponse status500

resp200Json
:: Encoder' a
-> a
:: ToJSON a
=> a
-> Response
resp200Json e =
resp200 JSON . encodeUtf8 .
E.simplePureEncodeTextNoSpaces e
resp200Json =
resp200 JSON . encode

-- |

Expand All @@ -142,8 +139,8 @@ handleRequest db rqType = case rqType of
-- handles all of that for us. Such is the pleasant nature of these
-- abstractions.
AddRq t c -> resp200 PlainText "Success" <$ DB.addCommentToTopic db t c
ViewRq t -> resp200Json (E.list encodeComment) <$> DB.getComments db t
ListRq -> resp200Json (E.list encodeTopic) <$> DB.getTopics db
ViewRq t -> resp200Json <$> DB.getComments db t
ListRq -> resp200Json <$> DB.getTopics db

mkRequest
:: Request
Expand Down
37 changes: 12 additions & 25 deletions src/Level05/Types.hs
Expand Up @@ -15,8 +15,6 @@ module Level05.Types
, getCommentText
, renderContentType
, fromDBComment
, encodeComment
, encodeTopic
) where

import GHC.Generics (Generic)
Expand All @@ -25,12 +23,11 @@ import GHC.Word (Word16)
import Data.ByteString (ByteString)
import Data.Text (Text, pack)

import System.IO.Error (IOError)

import Data.Functor.Contravariant ((>$<))
import Data.Aeson (ToJSON (..), object, (.=))
import Data.Monoid (Last,
Monoid (mappend, mempty))
import Data.Semigroup (Semigroup ((<>)))
import System.IO.Error (IOError)

import Data.List (stripPrefix)
import Data.Maybe (fromMaybe)
Expand All @@ -39,24 +36,19 @@ import qualified Data.Time.Format as TF

import Database.SQLite.SimpleErrors.Types (SQLiteResponse)

import Waargonaut.Encode (Encoder)
import qualified Waargonaut.Encode as E

import Level05.DB.Types (DBComment (dbCommentComment, dbCommentId, dbCommentTime, dbCommentTopic))

import Level05.Types.CommentText (CommentText,
encodeCommentText,
getCommentText,
mkCommentText)
import Level05.Types.Error (Error (DBError, EmptyCommentText, EmptyTopic, UnknownRoute))
import Level05.Types.Topic (Topic, encodeTopic,
getTopic, mkTopic)
import Level05.Types.Topic (Topic, getTopic, mkTopic)

newtype CommentId = CommentId Int
deriving (Show)

encodeCommentId :: Applicative f => Encoder f CommentId
encodeCommentId = (\(CommentId i) -> i) >$< E.int
instance ToJSON CommentId where
toJSON (CommentId i) = toJSON i

data Comment = Comment
{ commentId :: CommentId
Expand All @@ -66,18 +58,13 @@ data Comment = Comment
}
deriving Show

encodeISO8601DateTime :: Applicative f => Encoder f UTCTime
encodeISO8601DateTime = pack . TF.formatTime tl fmt >$< E.text
where
fmt = TF.iso8601DateFormat (Just "%H:%M:%S")
tl = TF.defaultTimeLocale { TF.knownTimeZones = [] }

encodeComment :: Applicative f => Encoder f Comment
encodeComment = E.mapLikeObj $ \c ->
E.atKey' "id" encodeCommentId (commentId c) .
E.atKey' "topic" encodeTopic (commentTopic c) .
E.atKey' "text" encodeCommentText (commentText c) .
E.atKey' "time" encodeISO8601DateTime (commentTime c)
instance ToJSON Comment where
toJSON c = object
[ "id" .= (commentId c)
, "topic" .= (commentTopic c)
, "text" .= (commentText c)
, "time" .= (commentTime c)
]

-- For safety we take our stored DBComment and try to construct a Comment that
-- we would be okay with showing someone. However unlikely it may be, this is a
Expand Down
14 changes: 5 additions & 9 deletions src/Level05/Types/CommentText.hs
Expand Up @@ -2,23 +2,19 @@ module Level05.Types.CommentText
( CommentText
, mkCommentText
, getCommentText
, encodeCommentText
) where

import Waargonaut.Encode (Encoder)
import qualified Waargonaut.Encode as E
import Level05.Types.Error (Error (EmptyCommentText), nonEmptyText)

import Level05.Types.Error (Error (EmptyCommentText),
nonEmptyText)
import Data.Aeson (ToJSON (..))

import Data.Functor.Contravariant ((>$<))
import Data.Text (Text)
import Data.Text (Text)

newtype CommentText = CommentText Text
deriving (Show)

encodeCommentText :: Applicative f => Encoder f CommentText
encodeCommentText = getCommentText >$< E.text
instance ToJSON CommentText where
toJSON (CommentText t) = toJSON t

mkCommentText
:: Text
Expand Down
13 changes: 5 additions & 8 deletions src/Level05/Types/Topic.hs
Expand Up @@ -2,22 +2,19 @@ module Level05.Types.Topic
(Topic
, mkTopic
, getTopic
, encodeTopic
) where

import Waargonaut.Encode (Encoder)
import qualified Waargonaut.Encode as E
import Level05.Types.Error (Error (EmptyTopic), nonEmptyText)

import Level05.Types.Error (Error (EmptyTopic), nonEmptyText)
import Data.Aeson (ToJSON (..))

import Data.Functor.Contravariant ((>$<))
import Data.Text (Text)
import Data.Text (Text)

newtype Topic = Topic Text
deriving Show

encodeTopic :: Applicative f => Encoder f Topic
encodeTopic = getTopic >$< E.text
instance ToJSON Topic where
toJSON (Topic t) = toJSON t

mkTopic
:: Text
Expand Down