Organize application logic by core/feature breakouts, add dhall configuration support and add a range of new config options in the Reader, add example configuration/deployment files

This commit is contained in:
James Eversole 2022-07-01 20:34:29 -05:00
parent b4d6e9c9d1
commit f7a61dcddc
15 changed files with 260 additions and 22 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
.stack-work
data/

7
.gitignore vendored
View File

@ -1,4 +1,7 @@
data/
bin/
/config.dhall
/Dockerfile
docker-stack.yml
.stack-work/ .stack-work/
./data
./config.dhall
*~ *~

View File

@ -19,13 +19,38 @@ extra-source-files:
library library
exposed-modules: exposed-modules:
Core.Configuration
Core.HTTP
Core.Mongo
Core.Types
Feature.Handlers
Lib Lib
other-modules: other-modules:
Paths_Purr Paths_Purr
hs-source-dirs: hs-source-dirs:
src src
default-extensions:
OverloadedStrings
GeneralizedNewtypeDeriving
DeriveGeneric
ConstraintKinds
FlexibleContexts
FlexibleInstances
ScopedTypeVariables
build-depends: build-depends:
base >=4.7 && <5 AesonBson ==0.4.1
, aeson >=2.0.0.0
, base >=4.7
, bson >=0.3
, dhall >=1.40 && <1.41.2
, http-types >=0.12.3
, iso8601-time >=0.1.5
, mongoDB >=2.7.1.1
, mtl >=2.2.2
, scotty ==0.12
, text >=1.2
, time >=1.9
, wai-extra >=3.1.12.1
default-language: Haskell2010 default-language: Haskell2010
executable Purr-exe executable Purr-exe
@ -34,10 +59,30 @@ executable Purr-exe
Paths_Purr Paths_Purr
hs-source-dirs: hs-source-dirs:
app app
default-extensions:
OverloadedStrings
GeneralizedNewtypeDeriving
DeriveGeneric
ConstraintKinds
FlexibleContexts
FlexibleInstances
ScopedTypeVariables
ghc-options: -threaded -rtsopts -with-rtsopts=-N ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: build-depends:
Purr AesonBson ==0.4.1
, base >=4.7 && <5 , Purr
, aeson >=2.0.0.0
, base >=4.7
, bson >=0.3
, dhall >=1.40 && <1.41.2
, http-types >=0.12.3
, iso8601-time >=0.1.5
, mongoDB >=2.7.1.1
, mtl >=2.2.2
, scotty ==0.12
, text >=1.2
, time >=1.9
, wai-extra >=3.1.12.1
default-language: Haskell2010 default-language: Haskell2010
test-suite Purr-test test-suite Purr-test
@ -47,8 +92,28 @@ test-suite Purr-test
Paths_Purr Paths_Purr
hs-source-dirs: hs-source-dirs:
test test
default-extensions:
OverloadedStrings
GeneralizedNewtypeDeriving
DeriveGeneric
ConstraintKinds
FlexibleContexts
FlexibleInstances
ScopedTypeVariables
ghc-options: -threaded -rtsopts -with-rtsopts=-N ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: build-depends:
Purr AesonBson ==0.4.1
, base >=4.7 && <5 , Purr
, aeson >=2.0.0.0
, base >=4.7
, bson >=0.3
, dhall >=1.40 && <1.41.2
, http-types >=0.12.3
, iso8601-time >=0.1.5
, mongoDB >=2.7.1.1
, mtl >=2.2.2
, scotty ==0.12
, text >=1.2
, time >=1.9
, wai-extra >=3.1.12.1
default-language: Haskell2010 default-language: Haskell2010

View File

@ -1,6 +1,7 @@
module Main where module Main where
import Lib import Prelude
import qualified Lib
main :: IO () main :: IO ()
main = someFunc main = Lib.main

11
examples/Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM haskell:9.0.2
WORKDIR /app
ADD . /app
RUN stack setup
RUN stack build --copy-bins --local-bin-path ./
EXPOSE 3000
CMD ./Purr-exe

10
examples/config.dhall Normal file
View File

@ -0,0 +1,10 @@
-- /config.dhall
{ hostname = "localhost"
, port = +3001
, environment = "development"
, adminDB = "admin"
, dataDB = "data"
, collection = "store"
, mongoUsername = "root"
, mongoPassword = "REPLACEME"
}

View File

@ -9,6 +9,15 @@ extra-source-files:
- README.md - README.md
- ChangeLog.md - ChangeLog.md
default-extensions:
- OverloadedStrings
- GeneralizedNewtypeDeriving
- DeriveGeneric
- ConstraintKinds
- FlexibleContexts
- FlexibleInstances
- ScopedTypeVariables
# Metadata used when publishing your package # Metadata used when publishing your package
# synopsis: Short description of your package # synopsis: Short description of your package
# category: Web # category: Web
@ -19,7 +28,19 @@ extra-source-files:
description: https://git.eversole.co/James/Purr description: https://git.eversole.co/James/Purr
dependencies: dependencies:
- base >= 4.7 && < 5 - aeson >= 2.0.0.0
- AesonBson == 0.4.1
- bson >= 0.3
- base >= 4.7
- dhall >= 1.40 && < 1.41.2
- http-types >= 0.12.3
- iso8601-time >= 0.1.5
- mongoDB >= 2.7.1.1
- mtl >= 2.2.2
- scotty == 0.12
- time >= 1.9
- text >= 1.2
- wai-extra >= 3.1.12.1
library: library:
source-dirs: src source-dirs: src

11
src/Core/Configuration.hs Normal file
View File

@ -0,0 +1,11 @@
module Core.Configuration ( main ) where
import Core.Types
import Dhall
instance FromDhall DhallConfig
main :: IO DhallConfig
main = do
input auto "./config.dhall"

14
src/Core/HTTP.hs Normal file
View File

@ -0,0 +1,14 @@
module Core.HTTP ( app ) where
import Core.Types
import Feature.Handlers as RES
import Network.Wai.Middleware.RequestLogger (logStdoutDev)
import Web.Scotty.Trans
app :: PurrApp ()
app = do
middleware logStdoutDev
get "/" RES.root

20
src/Core/Mongo.hs Normal file
View File

@ -0,0 +1,20 @@
module Core.Mongo ( mongoSetup, tempGetDocs ) where
import Core.Types
import Control.Monad.Reader (MonadIO)
import Database.MongoDB
import Prelude
mongoSetup :: DhallConfig -> IO MongoContext
mongoSetup conf = do
mongoCon <- connect $ host (hostname conf)
let adminCon = MongoContext mongoCon master (adminDB conf)
access (mongoPipe adminCon) (mongoAccessMode adminCon) (mongoDatabase adminCon) (auth (mongoUsername conf) (mongoPassword conf))
return $ MongoContext mongoCon master (dataDB conf)
tempGetDocs :: MonadIO m => AppConfig -> m [Document]
tempGetDocs appConfig = access (mongoPipe db) (mongoAccessMode db) (mongoDatabase db) $ find (select [] (collection conf)) >>= rest
where
db = dbconn appConfig
conf = res appConfig

34
src/Core/Types.hs Normal file
View File

@ -0,0 +1,34 @@
module Core.Types where
import qualified Data.Text.Lazy as LT
import Control.Monad.Reader (MonadIO, MonadReader, ReaderT)
import Data.Text
import Database.MongoDB (MongoContext)
import GHC.Generics (Generic)
import Numeric.Natural (Natural)
import Web.Scotty.Trans (ScottyT, ActionT)
type PurrApp a = ScottyT LT.Text ConfigM a
type PurrAction a = ActionT LT.Text ConfigM a
newtype ConfigM a = ConfigM
{ runConfigM :: ReaderT AppConfig IO a
} deriving (Applicative, Functor, Monad, MonadIO, MonadReader AppConfig)
data AppConfig = AppConfig
{ res :: DhallConfig
, dbconn :: MongoContext
}
data DhallConfig = DhallConfig
{ hostname :: String
, port :: Int
, environment :: Text
, adminDB :: Text
, dataDB :: Text
, collection :: Text
, mongoUsername :: Text
, mongoPassword :: Text
} deriving (Generic, Show)

18
src/Feature/Handlers.hs Normal file
View File

@ -0,0 +1,18 @@
module Feature.Handlers ( root ) where
import Core.Types
import qualified Core.Mongo as DB
import Control.Monad.Reader (ask, lift)
import Data.AesonBson (aesonify)
import Data.Bson (Document, Field (..), Value (Array, Doc))
import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import Web.Scotty.Trans (json)
import Prelude hiding (id)
root :: PurrAction ()
root = do
config <- lift $ ask
docs <- DB.tempGetDocs config
json $ aesonify ["allDocs" := Array (map Doc docs)]

View File

@ -1,6 +1,23 @@
module Lib module Lib ( main ) where
( someFunc
) where
someFunc :: IO () import Core.Types
someFunc = putStrLn "someFunc" import qualified Core.Configuration as Configuration
import qualified Core.HTTP as HTTP
import qualified Core.Mongo as DB
import Control.Monad.Reader (liftIO, runReaderT)
import Database.MongoDB (MongoContext)
import GHC.Natural (popCountNatural)
import Web.Scotty.Trans (scottyT)
import Prelude hiding (id)
main :: IO ()
main = do
dhallConf <- liftIO Configuration.main
dataDB <- liftIO $ DB.mongoSetup dhallConf
let config = AppConfig { res = dhallConf
, dbconn = dataDB
}
scottyT (port dhallConf) (flip runApp config) HTTP.app where
runApp :: ConfigM a -> AppConfig -> IO a
runApp m c = runReaderT (runConfigM m) c

View File

@ -35,12 +35,9 @@ packages:
# These entries can reference officially published versions as well as # These entries can reference officially published versions as well as
# forks / in-progress versions pinned to a git hash. For example: # forks / in-progress versions pinned to a git hash. For example:
# #
# extra-deps: extra-deps:
# - acme-missiles-0.3 - AesonBson-0.4.1@sha256:30a4ecb39e8da94dc1e1e8945eb0d4e33a833ae4342841b3c87c56b5918a90a1,1398
# - git: https://github.com/commercialhaskell/stack.git - bson-generic-0.0.9@sha256:ea6685daa618b2bbe6e189c33e195e812501baf42f53183eedc16f011690895a,817
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
#
# extra-deps: []
# Override default flag values for local packages and extra-deps # Override default flag values for local packages and extra-deps
# flags: {} # flags: {}

View File

@ -3,7 +3,21 @@
# For more information, please see the documentation at: # For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files # https://docs.haskellstack.org/en/stable/lock_files
packages: [] packages:
- completed:
hackage: AesonBson-0.4.1@sha256:30a4ecb39e8da94dc1e1e8945eb0d4e33a833ae4342841b3c87c56b5918a90a1,1398
pantry-tree:
size: 378
sha256: 10c7444de357f7fc1473542d8b3307811420889be32d5a2033d0dbc2d32a375d
original:
hackage: AesonBson-0.4.1@sha256:30a4ecb39e8da94dc1e1e8945eb0d4e33a833ae4342841b3c87c56b5918a90a1,1398
- completed:
hackage: bson-generic-0.0.9@sha256:ea6685daa618b2bbe6e189c33e195e812501baf42f53183eedc16f011690895a,817
pantry-tree:
size: 220
sha256: 46d452c35c2c762af25bf4d85b5248a94cbbe5b282bc4b1217d0ab3451011ae9
original:
hackage: bson-generic-0.0.9@sha256:ea6685daa618b2bbe6e189c33e195e812501baf42f53183eedc16f011690895a,817
snapshots: snapshots:
- completed: - completed:
size: 618740 size: 618740