-
Notifications
You must be signed in to change notification settings - Fork 80
/
Copy pathDb.purs
97 lines (77 loc) · 3.61 KB
/
Db.purs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
module Registry.App.Effect.Db where
import Registry.App.Prelude
import Data.Array as Array
import Data.DateTime (DateTime)
import Data.String as String
import Registry.API.V1 (JobId, LogLevel, LogLine)
import Registry.App.Effect.Log (LOG)
import Registry.App.Effect.Log as Log
import Registry.App.SQLite (JobResult, NewJob, SQLite)
import Registry.App.SQLite as SQLite
import Run (EFFECT, Run)
import Run as Run
-- We could separate these by database if it grows too large. Also, for now these
-- simply lift their Effect-based equivalents in the SQLite module, but ideally
-- that module would expose lower-level building blocks for accessing the database
-- and we'd implement these in terms of those in this module.
--
-- Also, this does not currently include setup and teardown (those are handled
-- outside the effect), but we may wish to add those in the future if they'll
-- be part of app code we want to test.
data Db a
= InsertLog LogLine a
| SelectLogsByJob JobId LogLevel (Maybe DateTime) (Array LogLine -> a)
| CreateJob NewJob a
| FinishJob JobResult a
| SelectJob JobId (Either String SQLite.Job -> a)
| RunningJobForPackage PackageName (Either String SQLite.Job -> a)
derive instance Functor Db
-- | An effect for accessing the database.
type DB r = (db :: Db | r)
_db :: Proxy "db"
_db = Proxy
-- | Insert a new log line into the database.
insertLog :: forall r. LogLine -> Run (DB + r) Unit
insertLog log = Run.lift _db (InsertLog log unit)
-- | Select all logs for a given job, filtered by loglevel and a time cutoff.
selectLogsByJob :: forall r. JobId -> LogLevel -> Maybe DateTime -> Run (DB + r) (Array LogLine)
selectLogsByJob jobId logLevel since = Run.lift _db (SelectLogsByJob jobId logLevel since identity)
-- | Create a new job in the database.
createJob :: forall r. NewJob -> Run (DB + r) Unit
createJob newJob = Run.lift _db (CreateJob newJob unit)
-- | Set a job in the database to the 'finished' state.
finishJob :: forall r. JobResult -> Run (DB + r) Unit
finishJob jobResult = Run.lift _db (FinishJob jobResult unit)
-- | Select a job by ID from the database.
selectJob :: forall r. JobId -> Run (DB + r) (Either String SQLite.Job)
selectJob jobId = Run.lift _db (SelectJob jobId identity)
-- | Select a job by package name from the database, failing if there is no
-- | current job available for that package name.
runningJobForPackage :: forall r. PackageName -> Run (DB + r) (Either String SQLite.Job)
runningJobForPackage name = Run.lift _db (RunningJobForPackage name identity)
interpret :: forall r a. (Db ~> Run r) -> Run (DB + r) a -> Run r a
interpret handler = Run.interpret (Run.on _db handler Run.send)
type SQLiteEnv = { db :: SQLite }
-- | Interpret DB by interacting with the SQLite database on disk.
handleSQLite :: forall r a. SQLiteEnv -> Db a -> Run (LOG + EFFECT + r) a
handleSQLite env = case _ of
InsertLog log next -> do
Run.liftEffect $ SQLite.insertLog env.db log
pure next
SelectLogsByJob jobId logLevel since reply -> do
logs <- Run.liftEffect $ SQLite.selectLogsByJob env.db jobId logLevel since
unless (Array.null logs.fail) do
Log.warn $ "Some logs are not readable: " <> String.joinWith "\n" logs.fail
pure $ reply logs.success
CreateJob newJob next -> do
Run.liftEffect $ SQLite.createJob env.db newJob
pure next
FinishJob jobResult next -> do
Run.liftEffect $ SQLite.finishJob env.db jobResult
pure next
SelectJob jobId reply -> do
job <- Run.liftEffect $ SQLite.selectJob env.db jobId
pure $ reply job
RunningJobForPackage name reply -> do
job <- Run.liftEffect $ SQLite.runningJobForPackage env.db name
pure $ reply job